[Libreoffice-commits] core.git: Branch 'feature/calc-group-interpreter' - 44 commits - configure.ac download.lst mdds/0001-Remove-disambiguation-of-a-integer-type.patch mdds/UnpackedTarball_mdds.mk sc/inc sc/Library_sc.mk sc/qa sc/source

Kohei Yoshida kohei.yoshida at gmail.com
Mon May 20 17:09:21 PDT 2013


 configure.ac                                            |    4 
 download.lst                                            |    2 
 mdds/0001-Remove-disambiguation-of-a-integer-type.patch |   25 
 mdds/UnpackedTarball_mdds.mk                            |    1 
 sc/Library_sc.mk                                        |    3 
 sc/inc/clipcontext.hxx                                  |  100 ++
 sc/inc/column.hxx                                       |   79 +-
 sc/inc/columniterator.hxx                               |   10 
 sc/inc/columnspanset.hxx                                |   56 +
 sc/inc/conditio.hxx                                     |    1 
 sc/inc/document.hxx                                     |   42 +
 sc/inc/documentimport.hxx                               |    9 
 sc/inc/formulacell.hxx                                  |    9 
 sc/inc/listenercontext.hxx                              |   53 +
 sc/inc/mtvelements.hxx                                  |   52 +
 sc/inc/refdata.hxx                                      |    2 
 sc/inc/table.hxx                                        |   59 +
 sc/inc/textuno.hxx                                      |    4 
 sc/qa/unit/ucalc.cxx                                    |  401 +++++++++++
 sc/source/core/data/clipcontext.cxx                     |  102 +++
 sc/source/core/data/column.cxx                          |   86 +-
 sc/source/core/data/column2.cxx                         |  257 +++++++
 sc/source/core/data/column3.cxx                         |  278 +++++---
 sc/source/core/data/columnspanset.cxx                   |  103 +++
 sc/source/core/data/conditio.cxx                        |   12 
 sc/source/core/data/documen2.cxx                        |    7 
 sc/source/core/data/documen4.cxx                        |   40 -
 sc/source/core/data/documen6.cxx                        |   58 +
 sc/source/core/data/documen7.cxx                        |   33 
 sc/source/core/data/document.cxx                        |  544 ++++++++--------
 sc/source/core/data/documentimport.cxx                  |   64 +
 sc/source/core/data/formulacell.cxx                     |  249 ++++---
 sc/source/core/data/listenercontext.cxx                 |   78 ++
 sc/source/core/data/mtvelements.cxx                     |   40 +
 sc/source/core/data/table1.cxx                          |   26 
 sc/source/core/data/table2.cxx                          |   91 +-
 sc/source/core/data/table5.cxx                          |   16 
 sc/source/core/tool/refdata.cxx                         |   11 
 sc/source/ui/unoobj/textuno.cxx                         |    1 
 sc/source/ui/view/viewfunc.cxx                          |   12 
 40 files changed, 2328 insertions(+), 692 deletions(-)

New commits:
commit 37676225bd5f69da077d2ba3f9d25fa211af624e
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 19:21:21 2013 -0400

    Rehash the 0.8.1 package due to a last minute oops.
    
    Change-Id: Idea947e8b6674d436cc1a1d92f29279d2c03fc58

diff --git a/download.lst b/download.lst
index f6c10e7..b0221ae 100644
--- a/download.lst
+++ b/download.lst
@@ -63,7 +63,7 @@ export LIBXML_TARBALL := 7740a8ec23878a2f50120e1faa2730f2-libxml2-2.7.6.tar.gz
 export LIBXSLT_TARBALL := e61d0364a30146aaa3001296f853b2b9-libxslt-1.1.26.tar.gz
 export LPSOLVE_TARBALL := 26b3e95ddf3d9c077c480ea45874b3b8-lp_solve_5.5.tar.gz
 export MARIADB_TARBALL := 05f84c95b610c21c5fd510d10debcabf-mariadb-native-client-1.0.0.tar.bz2
-export MDDS_TARBALL := 5c7060bfa4ebb77b86026c268e9681fa-mdds_0.8.1.tar.bz2
+export MDDS_TARBALL := 08c85a6d6d793daee14e10e22eefdc4b-mdds_0.8.1.tar.bz2
 export MYSQLCPPCONN_TARBALL := 0981bda6548a8c8233ffce2b6e4b2a23-mysql-connector-c++-1.1.0.tar.gz
 export MYTHES_TARBALL := 46e92b68e31e858512b680b3b61dc4c1-mythes-1.2.3.tar.gz
 export NEON_TARBALL := ff369e69ef0f0143beb5626164e87ae2-neon-0.29.5.tar.gz
commit fd2de0b8075556fa08a7317eb9079d4192409b95
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 18:23:10 2013 -0400

    We don't need this patch anymore.
    
    Change-Id: Id6b787096d15c40903661fa3d309e8131b655752

diff --git a/mdds/0001-Remove-disambiguation-of-a-integer-type.patch b/mdds/0001-Remove-disambiguation-of-a-integer-type.patch
deleted file mode 100644
index e23d057..0000000
--- a/mdds/0001-Remove-disambiguation-of-a-integer-type.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From 46cf3188790b821b359e13b14e2211898ab2139b Mon Sep 17 00:00:00 2001
-From: Kohei Yoshida <kohei.yoshida at gmail.com>
-Date: Thu, 9 May 2013 14:48:41 -0400
-Subject: [PATCH] Remove disambiguation of a integer type.
-
----
- include/mdds/multi_type_vector_def.inl | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/include/mdds/multi_type_vector_def.inl b/include/mdds/multi_type_vector_def.inl
-index 2299a7b..f443e08 100644
---- a/include/mdds/multi_type_vector_def.inl
-+++ misc/build/mdds_0.8.0/include/mdds/multi_type_vector_def.inl
-@@ -1479,7 +1479,7 @@ multi_type_vector<_CellBlockFunc>::transfer_single_block(
- 
-         // Insert two new blocks below current.
-         size_type blk2_size = blk_dest->m_size - dest_pos_in_block - len;
--        dest.m_blocks.insert(dest.m_blocks.begin()+dest_block_index+1, 2, NULL);
-+        dest.m_blocks.insert(dest.m_blocks.begin()+dest_block_index+1, 2u, NULL);
-         dest.m_blocks[dest_block_index+1] = new block(len);
-         dest.m_blocks[dest_block_index+2] = new block(blk2_size);
-         blk_dest->m_size = dest_pos_in_block;
--- 
-1.8.0
-
diff --git a/mdds/UnpackedTarball_mdds.mk b/mdds/UnpackedTarball_mdds.mk
index 11a6a23..b33bc1b 100644
--- a/mdds/UnpackedTarball_mdds.mk
+++ b/mdds/UnpackedTarball_mdds.mk
@@ -17,7 +17,6 @@ $(eval $(call gb_UnpackedTarball_add_patches,mdds,\
 	mdds/mdds_0.6.0.patch \
 	mdds/0001-Workaround-for-gcc-bug.patch \
 	mdds/mdds_0.7.0_unreachable_warning.patch.1 \
-	mdds/0001-Remove-disambiguation-of-a-integer-type.patch \
 ))
 
 # vim: set noet sw=4 ts=4:
commit c22906cee3d9ddc301c053f548ba77039d052919
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 18:19:48 2013 -0400

    Update mdds to 0.8.1.
    
    Change-Id: If6c1a8a88e9996527d3fcf350a00a72a3866ef4f

diff --git a/configure.ac b/configure.ac
index e84f928..53c6d9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8098,9 +8098,9 @@ if test "$with_system_mdds" = "yes"; then
     save_CPPFLAGS="$CPPFLAGS"
     CPPFLAGS="$CPPFLAGS $MDDS_CPPFLAGS"
     AC_CHECK_HEADER(mdds/multi_type_vector.hpp, [],
-        [AC_MSG_ERROR(mdds/multi_type_vector.hpp not found. Install mdds >= 0.8.0)], [])
+        [AC_MSG_ERROR(mdds/multi_type_vector.hpp not found. Install mdds >= 0.8.1)], [])
     AC_CHECK_TYPE(mdds::multi_type_vector<int>::iterator, [],
-        [AC_MSG_ERROR(mdds/multi_type_matrix.hpp does not define multi_type_vector::iterator. Install mdds >= 0.8.0)], [#include <mdds/multi_type_vector.hpp>])
+        [AC_MSG_ERROR(mdds/multi_type_matrix.hpp does not define multi_type_vector::iterator. Install mdds >= 0.8.1)], [#include <mdds/multi_type_vector.hpp>])
     CPPFLAGS="$save_CPPFLAGS"
     AC_LANG_POP([C++])
 else
diff --git a/download.lst b/download.lst
index 6cfcfe5..f6c10e7 100644
--- a/download.lst
+++ b/download.lst
@@ -63,7 +63,7 @@ export LIBXML_TARBALL := 7740a8ec23878a2f50120e1faa2730f2-libxml2-2.7.6.tar.gz
 export LIBXSLT_TARBALL := e61d0364a30146aaa3001296f853b2b9-libxslt-1.1.26.tar.gz
 export LPSOLVE_TARBALL := 26b3e95ddf3d9c077c480ea45874b3b8-lp_solve_5.5.tar.gz
 export MARIADB_TARBALL := 05f84c95b610c21c5fd510d10debcabf-mariadb-native-client-1.0.0.tar.bz2
-export MDDS_TARBALL := b0bba8c768f3d92608a07149039510e5-mdds_0.8.0.tar.bz2
+export MDDS_TARBALL := 5c7060bfa4ebb77b86026c268e9681fa-mdds_0.8.1.tar.bz2
 export MYSQLCPPCONN_TARBALL := 0981bda6548a8c8233ffce2b6e4b2a23-mysql-connector-c++-1.1.0.tar.gz
 export MYTHES_TARBALL := 46e92b68e31e858512b680b3b61dc4c1-mythes-1.2.3.tar.gz
 export NEON_TARBALL := ff369e69ef0f0143beb5626164e87ae2-neon-0.29.5.tar.gz
commit a2e6d3b43d438d34ae7f58b018bd4d2af5db3847
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 17:24:50 2013 -0400

    Test performance of pasting of formula cells as well.
    
    Undo and redo of this are still a bit slow. Not fast enough to be
    reliably tested.
    
    Change-Id: I0e0b4c16e55bae60f8d7b1db71347a93eb977de8

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 62d6546..59201d4 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -702,6 +702,100 @@ void Test::testPerf()
         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     }
 
+    clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
+    CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
+    CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
+
+    {
+        // Measure the performance of repeat-pasting 2 adjacent cells to a
+        // large cell range, undoing it, and redoing it.  The bottom one of
+        // the two source cells is empty.  In this scenario, the non-empty
+        // cell is a formula cell referencing a cell to the right, which
+        // inserts a broadcaster to cell it references. So it has a higher
+        // overhead than the previous scenario.
+
+        ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
+
+        ScAddress aPos(0,0,0);
+        m_pDoc->SetString(aPos, "=B1");
+        ScMarkData aMark;
+        aMark.SetMarkArea(aSrcRange);
+
+        // Copy to clipboard.
+        ScDocument aClipDoc(SCDOCMODE_CLIP);
+        ScClipParam aParam(aSrcRange, false);
+        m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
+
+        ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pUndoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
+
+        // Paste it to A3:A50001, and measure its duration.
+        ScRange aPasteRange(0,2,0,0,50000,0);
+        aMark.SetMarkArea(aPasteRange);
+
+        {
+            MeasureTimeSwitch aTime(diff);
+            m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Pasting took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pRedoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
+
+        // Create an undo object for this.
+        ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
+        ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
+
+        // Make sure it did what it's supposed to do.
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
+        ScAddress aTmp = aPasteRange.aStart;
+        aTmp.IncRow();
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
+
+#if 0 // TODO: Undo and redo of this scenario is currently not fast enough to be tested reliably.
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Undo();
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Undoing took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really undone.
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPos));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
+
+        // Now redo.
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Redo();
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Redoing took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really redone.
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_FORMULA, m_pDoc->GetCellType(aPasteRange.aEnd));
+#endif
+    }
+
     m_pDoc->DeleteTab(0);
 }
 
commit 40ab7c3b3d9106400e7c7cb208204debc56dc755
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 17:01:12 2013 -0400

    Another performance scenario. Pasting of cells interspersed with empty ones.
    
    Change-Id: Ia03af65dc1daf13e1228cacc20ce931839305ab8

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 3506a29..62d6546 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -535,6 +535,9 @@ void Test::testPerf()
     CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
 
     {
+        // Measure the performance of repeat-pasting a single cell to a large
+        // cell range, undoing it, and redoing it.
+
         ScAddress aPos(0,0,0);
         m_pDoc->SetString(aPos, "test");
         ScMarkData aMark;
@@ -610,6 +613,95 @@ void Test::testPerf()
         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
     }
 
+    clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
+    CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
+    CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
+
+    {
+        // Measure the performance of repeat-pasting 2 adjacent cells to a
+        // large cell range, undoing it, and redoing it.  The bottom one of
+        // the two source cells is empty.
+
+        ScRange aSrcRange(0,0,0,0,1,0); // A1:A2
+
+        ScAddress aPos(0,0,0);
+        m_pDoc->SetValue(aPos, 12);
+        ScMarkData aMark;
+        aMark.SetMarkArea(aSrcRange);
+
+        // Copy to clipboard.
+        ScDocument aClipDoc(SCDOCMODE_CLIP);
+        ScClipParam aParam(aSrcRange, false);
+        m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
+
+        ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pUndoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(aSrcRange, IDF_CONTENTS, false, pUndoDoc, &aMark);
+
+        // Paste it to A3:A100001, and measure its duration.
+        ScRange aPasteRange(0,2,0,0,100000,0);
+        aMark.SetMarkArea(aPasteRange);
+
+        {
+            MeasureTimeSwitch aTime(diff);
+            m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Pasting A1:A2 to A3:A100001 took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pRedoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
+
+        // Create an undo object for this.
+        ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
+        ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
+
+        // Make sure it did what it's supposed to do.
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
+        ScAddress aTmp = aPasteRange.aStart;
+        aTmp.IncRow();
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aTmp));
+
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Undo();
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Undoing took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really undone.
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_VALUE, m_pDoc->GetCellType(aPos));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
+
+        // Now redo.
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Redo();
+        }
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Redoing took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really redone.
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
+    }
+
     m_pDoc->DeleteTab(0);
 }
 
commit 27c980d12d64971bc35d6f93cc62fb68c89e005f
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 16:35:20 2013 -0400

    A bit cleaner way to measure time segment.
    
    Change-Id: I999baaa3acba5513a148542fa28f7ae43dd7ee4f

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index cbd2752..3506a29 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -442,6 +442,25 @@ public:
     }
 };
 
+class MeasureTimeSwitch
+{
+    double& mrDiff;
+    TimeValue maTimeBefore;
+public:
+    MeasureTimeSwitch(double& rDiff) : mrDiff(rDiff)
+    {
+        mrDiff = 9999.0;
+        osl_getSystemTime(&maTimeBefore);
+    }
+
+    ~MeasureTimeSwitch()
+    {
+        TimeValue aTimeAfter;
+        osl_getSystemTime(&aTimeAfter);
+        mrDiff = getTimeDiff(aTimeAfter, maTimeBefore);
+    }
+};
+
 Test::Test()
     : m_pDoc(0)
 {
@@ -471,15 +490,15 @@ void Test::testPerf()
 {
     CPPUNIT_ASSERT_MESSAGE ("failed to insert sheet", m_pDoc->InsertTab (0, "foo"));
 
-    TimeValue aTimeBefore, aTimeAfter;
+    double diff = 9999.0;
 
     // Clearing an already empty sheet should finish in a fraction of a
     // second.  Flag failure if it takes more than one second.  Clearing 100
     // columns should be large enough to flag if something goes wrong.
-    osl_getSystemTime(&aTimeBefore);
-    clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
-    osl_getSystemTime(&aTimeAfter);
-    double diff = getTimeDiff(aTimeAfter, aTimeBefore);
+    {
+        MeasureTimeSwitch aTime(diff);
+        clearRange(m_pDoc, ScRange(0,0,0,99,MAXROW,0));
+    }
     if (diff >= 1.0)
     {
         std::ostringstream os;
@@ -499,10 +518,10 @@ void Test::testPerf()
 
         // Now, Delete B2:B100000. This should complete in a fraction of a second
         // (0.06 sec on my machine).
-        osl_getSystemTime(&aTimeBefore);
-        clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
-        osl_getSystemTime(&aTimeAfter);
-        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        {
+            MeasureTimeSwitch aTime(diff);
+            clearRange(m_pDoc, ScRange(1,1,0,1,99999,0));
+        }
         if (diff >= 1.0)
         {
             std::ostringstream os;
@@ -535,10 +554,10 @@ void Test::testPerf()
         ScRange aPasteRange(0,1,0,0,99999,0);
         aMark.SetMarkArea(aPasteRange);
 
-        osl_getSystemTime(&aTimeBefore);
-        m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
-        osl_getSystemTime(&aTimeAfter);
-        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        {
+            MeasureTimeSwitch aTime(diff);
+            m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
+        }
         if (diff >= 1.0)
         {
             std::ostringstream os;
@@ -558,10 +577,10 @@ void Test::testPerf()
         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
         CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
 
-        osl_getSystemTime(&aTimeBefore);
-        aUndo.Undo();
-        osl_getSystemTime(&aTimeAfter);
-        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Undo();
+        }
         if (diff >= 1.0)
         {
             std::ostringstream os;
@@ -575,10 +594,10 @@ void Test::testPerf()
         CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
 
         // Now redo.
-        osl_getSystemTime(&aTimeBefore);
-        aUndo.Redo();
-        osl_getSystemTime(&aTimeAfter);
-        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        {
+            MeasureTimeSwitch aTime(diff);
+            aUndo.Redo();
+        }
         if (diff >= 1.0)
         {
             std::ostringstream os;
commit db4020245ff2b0491563ded33dcd20505d2907e3
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 16:19:04 2013 -0400

    Make these stack variables & fix one memory leak with the undo object.
    
    Change-Id: I1dd2bf0fc843394502119928c921913131c86f7e

diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 5ebf6b6..cbd2752 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -5684,8 +5684,8 @@ void Test::testCopyPaste()
     ScClipParam aClipParam(aRange, false);
     ScMarkData aMark;
     aMark.SetMarkArea(aRange);
-    ScDocument* pClipDoc = new ScDocument(SCDOCMODE_CLIP);
-    m_pDoc->CopyToClip(aClipParam, pClipDoc, &aMark);
+    ScDocument aClipDoc(SCDOCMODE_CLIP);
+    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aMark);
 
     sal_uInt16 nFlags = IDF_ALL;
     aRange = ScRange(0,1,1,2,1,1);//target: Sheet2.A2:C2
@@ -5694,9 +5694,9 @@ void Test::testCopyPaste()
     ScMarkData aMarkData2;
     aMarkData2.SetMarkArea(aRange);
     ScRefUndoData* pRefUndoData= new ScRefUndoData(m_pDoc);
-    SfxUndoAction* pUndo = new ScUndoPaste(
+    ScUndoPaste aUndo(
         &m_xDocShRef, ScRange(0, 1, 1, 2, 1, 1), aMarkData2, pUndoDoc, NULL, IDF_ALL, pRefUndoData, false);
-    m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, pClipDoc);
+    m_pDoc->CopyFromClip(aRange, aMarkData2, nFlags, NULL, &aClipDoc);
 
     //check values after copying
     OUString aString;
@@ -5718,13 +5718,13 @@ void Test::testCopyPaste()
 
 
     //check undo and redo
-    pUndo->Undo();
+    aUndo.Undo();
     m_pDoc->GetValue(1,1,1, aValue);
     ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", aValue, 0);
     aString = m_pDoc->GetString(2, 1, 1);
     CPPUNIT_ASSERT_MESSAGE("after undo string should be removed", aString.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("")));
 
-    pUndo->Redo();
+    aUndo.Redo();
     m_pDoc->GetValue(1,1,1, aValue);
     ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 2 after redo", aValue, 2);
     aString = m_pDoc->GetString(2, 1, 1);
@@ -5732,9 +5732,6 @@ void Test::testCopyPaste()
     m_pDoc->GetFormula(1,1,1, aString);
     CPPUNIT_ASSERT_MESSAGE("Formula should be correct again", aString == aFormulaString);
 
-    //clear all variables
-    delete pClipDoc;
-    delete pUndoDoc;
     m_pDoc->DeleteTab(1);
     m_pDoc->DeleteTab(0);
 }
commit 3c8cec77b07486052c3390432956dd02be08a20e
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 16:13:32 2013 -0400

    Add performance test for repeat-pasting cell to a large cell range.
    
    Change-Id: I98dcdb1e0a72f2c3ad6f33c7b6b7d4a4bcf89096

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index d1492eb..dfc46b0 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -472,6 +472,7 @@ public:
     SvtBroadcaster* GetBroadcaster( SCROW nRow );
     const SvtBroadcaster* GetBroadcaster( SCROW nRow ) const;
     void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 );
+    bool HasBroadcaster() const;
 
 private:
     void UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow );
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index ff525a6..f5d3414 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1972,6 +1972,11 @@ public:
     const SvtBroadcaster* GetBroadcaster( const ScAddress& rPos ) const;
     void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength );
 
+    /**
+     * See if specified column has any broadcaster at all.
+     */
+    bool HasBroadcaster( SCTAB nTab, SCCOL nCol ) const;
+
 private: // CLOOK-Impl-methods
 
     /**
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 6038311..b62fa1e 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -851,6 +851,7 @@ public:
     SvtBroadcaster* GetBroadcaster( SCCOL nCol, SCROW nRow );
     const SvtBroadcaster* GetBroadcaster( SCCOL nCol, SCROW nRow ) const;
     void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 );
+    bool HasBroadcaster( SCCOL nCol ) const;
 
     /** Replace behaves differently to the Search; adjust the rCol and rRow accordingly.
 
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 07fa33f..5ebf6b6 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -511,6 +511,86 @@ void Test::testPerf()
         }
     }
 
+    clearRange(m_pDoc, ScRange(0,0,0,1,MAXROW,0)); // Clear columns A:B.
+    CPPUNIT_ASSERT_MESSAGE("Column A shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,0));
+    CPPUNIT_ASSERT_MESSAGE("Column B shouldn't have any broadcasters.", !m_pDoc->HasBroadcaster(0,1));
+
+    {
+        ScAddress aPos(0,0,0);
+        m_pDoc->SetString(aPos, "test");
+        ScMarkData aMark;
+        aMark.SelectOneTable(0);
+
+        // Copy cell A1 to clipboard.
+        ScDocument aClipDoc(SCDOCMODE_CLIP);
+        ScClipParam aParam(aPos, false);
+        m_pDoc->CopyToClip(aParam, &aClipDoc, &aMark);
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), aClipDoc.GetString(aPos));
+
+        ScDocument* pUndoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pUndoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(ScRange(aPos), IDF_CONTENTS, false, pUndoDoc, &aMark);
+
+        // Paste it to A2:A100000, and measure its duration.
+        ScRange aPasteRange(0,1,0,0,99999,0);
+        aMark.SetMarkArea(aPasteRange);
+
+        osl_getSystemTime(&aTimeBefore);
+        m_pDoc->CopyFromClip(aPasteRange, aMark, IDF_CONTENTS, pUndoDoc, &aClipDoc);
+        osl_getSystemTime(&aTimeAfter);
+        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Pasting a single cell to A2:A100000 took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        ScDocument* pRedoDoc = new ScDocument(SCDOCMODE_UNDO);
+        pRedoDoc->InitUndo(m_pDoc, 0, 0);
+        m_pDoc->CopyToDocument(aPasteRange, IDF_CONTENTS, false, pRedoDoc, &aMark);
+
+        // Create an undo object for this.
+        ScRefUndoData* pRefUndoData = new ScRefUndoData(m_pDoc);
+        ScUndoPaste aUndo(&(*m_xDocShRef), aPasteRange, aMark, pUndoDoc, pRedoDoc, IDF_CONTENTS, pRefUndoData);
+
+        // Make sure it did what it's supposed to do.
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
+
+        osl_getSystemTime(&aTimeBefore);
+        aUndo.Undo();
+        osl_getSystemTime(&aTimeAfter);
+        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Undoing a pasting of a cell to A2:A100000 took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really undone.
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_STRING, m_pDoc->GetCellType(aPos));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(aPasteRange.aEnd));
+
+        // Now redo.
+        osl_getSystemTime(&aTimeBefore);
+        aUndo.Redo();
+        osl_getSystemTime(&aTimeAfter);
+        diff = getTimeDiff(aTimeAfter, aTimeBefore);
+        if (diff >= 1.0)
+        {
+            std::ostringstream os;
+            os << "Redoing a pasting of a cell to A2:A100000 took " << diff << " seconds. It should be instant.";
+            CPPUNIT_FAIL(os.str().c_str());
+        }
+
+        // Make sure it's really redone.
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aStart));
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aPos), m_pDoc->GetString(aPasteRange.aEnd));
+    }
+
     m_pDoc->DeleteTab(0);
 }
 
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 43a0ee5..fcf21b0 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -1611,6 +1611,18 @@ void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRo
         maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
 }
 
+bool ScColumn::HasBroadcaster() const
+{
+    sc::BroadcasterStoreType::const_iterator it = maBroadcasters.begin(), itEnd = maBroadcasters.end();
+    for (; it != itEnd; ++it)
+    {
+        if (it->type == sc::element_type_broadcaster)
+            // Having a broadcaster block automatically means there is at least one broadcaster.
+            return true;
+    }
+    return false;
+}
+
 sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
 {
     return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnTextWidth;
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 4b63e7a..fe7028d 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2254,6 +2254,15 @@ void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const S
     pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
 }
 
+bool ScDocument::HasBroadcaster( SCTAB nTab, SCCOL nCol ) const
+{
+    const ScTable* pTab = FetchTable(nTab);
+    if (!pTab)
+        return false;
+
+    return pTab->HasBroadcaster(nCol);
+}
+
 bool ScDocument::TableExists( SCTAB nTab ) const
 {
     return ValidTab(nTab) && static_cast<size_t>(nTab) < maTabs.size() && maTabs[nTab];
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index 9c380c8..c0e2c3f 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -2186,6 +2186,14 @@ void ScTable::DeleteBroadcasters(
     aCol[nCol].DeleteBroadcasters(rBlockPos, nRow1, nRow2);
 }
 
+bool ScTable::HasBroadcaster( SCCOL nCol ) const
+{
+    if (!ValidCol(nCol))
+        return false;
+
+    return aCol[nCol].HasBroadcaster();
+}
+
 const SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) const
 {
     if (!ValidColRow(nCol, nRow))
commit c8f8d7dc66a05675d775c46d9bff7d3cf74d36cb
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 13:17:28 2013 -0400

    Ditto when purging broadcasters.
    
    Change-Id: I632d617cad76485f7e1f57daa7db4d4cfa775e8b

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 6db56d4..d1492eb 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -471,7 +471,7 @@ public:
 
     SvtBroadcaster* GetBroadcaster( SCROW nRow );
     const SvtBroadcaster* GetBroadcaster( SCROW nRow ) const;
-    void DeleteBroadcasters( SCROW nRow1, SCROW nRow2 );
+    void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 );
 
 private:
     void UpdateScriptType( sc::CellTextAttr& rAttr, SCROW nRow );
diff --git a/sc/inc/columnspanset.hxx b/sc/inc/columnspanset.hxx
index 35d94bf..ab24828 100644
--- a/sc/inc/columnspanset.hxx
+++ b/sc/inc/columnspanset.hxx
@@ -47,7 +47,6 @@ public:
     void set(SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, bool bVal);
 
     void executeFromTop(Action& ac) const;
-    void executeFromBottom(Action& ac) const;
 };
 
 }
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 544c9b5..ff525a6 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1970,7 +1970,7 @@ public:
 
     SvtBroadcaster* GetBroadcaster( const ScAddress& rPos );
     const SvtBroadcaster* GetBroadcaster( const ScAddress& rPos ) const;
-    void DeleteBroadcasters( const ScAddress& rTopPos, SCROW nLength );
+    void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength );
 
 private: // CLOOK-Impl-methods
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 48fca53..6038311 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -850,7 +850,7 @@ public:
 
     SvtBroadcaster* GetBroadcaster( SCCOL nCol, SCROW nRow );
     const SvtBroadcaster* GetBroadcaster( SCCOL nCol, SCROW nRow ) const;
-    void DeleteBroadcasters( SCCOL nCol, SCROW nRow1, SCROW nRow2 );
+    void DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 );
 
     /** Replace behaves differently to the Search; adjust the rCol and rRow accordingly.
 
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 324519a..43a0ee5 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -1605,9 +1605,10 @@ const SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow) const
     return maBroadcasters.get<SvtBroadcaster*>(nRow);
 }
 
-void ScColumn::DeleteBroadcasters( SCROW nRow1, SCROW nRow2 )
+void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 )
 {
-    maBroadcasters.set_empty(nRow1, nRow2);
+    rBlockPos.miBroadcasterPos =
+        maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
 }
 
 sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
diff --git a/sc/source/core/data/columnspanset.cxx b/sc/source/core/data/columnspanset.cxx
index ea4f706..9b63dc7 100644
--- a/sc/source/core/data/columnspanset.cxx
+++ b/sc/source/core/data/columnspanset.cxx
@@ -85,43 +85,14 @@ void ColumnSpanSet::executeFromTop(Action& ac) const
             ColumnSpansType::const_iterator it = rCol.begin(), itEnd = rCol.end();
             SCROW nRow1, nRow2;
             nRow1 = it->first;
+            bool bVal = it->second;
             for (++it; it != itEnd; ++it)
             {
                 nRow2 = it->first-1;
-                bool bVal = it->second;
                 ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal);
 
                 nRow1 = nRow2+1; // for the next iteration.
-            }
-        }
-    }
-}
-
-void ColumnSpanSet::executeFromBottom(Action& ac) const
-{
-    for (size_t nTab = 0; nTab < maDoc.size(); ++nTab)
-    {
-        if (!maDoc[nTab])
-            continue;
-
-        const TableType& rTab = *maDoc[nTab];
-        for (size_t nCol = 0; nCol < rTab.size(); ++nCol)
-        {
-            if (!rTab[nCol])
-                continue;
-
-            ac.startColumn(nTab, nCol);
-            ColumnSpansType& rCol = *rTab[nCol];
-            ColumnSpansType::const_reverse_iterator it = rCol.rbegin(), itEnd = rCol.rend();
-            SCROW nRow1, nRow2;
-            nRow2 = it->first-1;
-            for (++it; it != itEnd; ++it)
-            {
-                nRow1 = it->first;
-                bool bVal = it->second;
-                ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal);
-
-                nRow2 = nRow1-1; // for the next iteration.
+                bVal = it->second;
             }
         }
     }
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index d9145b0..4b63e7a 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2238,20 +2238,20 @@ SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos )
 
 const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const
 {
-    ScTable* pTab = FetchTable(rPos.Tab());
+    const ScTable* pTab = FetchTable(rPos.Tab());
     if (!pTab)
         return NULL;
 
     return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
 }
 
-void ScDocument::DeleteBroadcasters( const ScAddress& rTopPos, SCROW nLength )
+void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength )
 {
     ScTable* pTab = FetchTable(rTopPos.Tab());
     if (!pTab || nLength <= 0)
         return;
 
-    pTab->DeleteBroadcasters(rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
+    pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
 }
 
 bool ScDocument::TableExists( SCTAB nTab ) const
diff --git a/sc/source/core/data/listenercontext.cxx b/sc/source/core/data/listenercontext.cxx
index f53f1bc..1f4c995 100644
--- a/sc/source/core/data/listenercontext.cxx
+++ b/sc/source/core/data/listenercontext.cxx
@@ -17,12 +17,22 @@ namespace {
 class PurgeAction : public ColumnSpanSet::Action
 {
     ScDocument& mrDoc;
+    sc::ColumnBlockPosition maBlockPos;
+
 public:
     PurgeAction(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+    virtual void startColumn(SCTAB nTab, SCCOL nCol)
+    {
+        mrDoc.InitColumnBlockPosition(maBlockPos, nTab, nCol);
+    }
+
     virtual void execute(const ScAddress& rPos, SCROW nLength, bool bVal)
     {
         if (bVal)
-            mrDoc.DeleteBroadcasters(rPos, nLength);
+        {
+            mrDoc.DeleteBroadcasters(maBlockPos, rPos, nLength);
+        }
     };
 };
 
@@ -60,7 +70,7 @@ void EndListeningContext::addEmptyBroadcasterPosition(SCTAB nTab, SCCOL nCol, SC
 void EndListeningContext::purgeEmptyBroadcasters()
 {
     PurgeAction aAction(mrDoc);
-    maSet.executeFromBottom(aAction);
+    maSet.executeFromTop(aAction);
 }
 
 }
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index d1654a6..9c380c8 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -2177,12 +2177,13 @@ SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow )
     return aCol[nCol].GetBroadcaster(nRow);
 }
 
-void ScTable::DeleteBroadcasters( SCCOL nCol, SCROW nRow1, SCROW nRow2 )
+void ScTable::DeleteBroadcasters(
+    sc::ColumnBlockPosition& rBlockPos, SCCOL nCol, SCROW nRow1, SCROW nRow2 )
 {
     if (!ValidCol(nCol))
         return;
 
-    aCol[nCol].DeleteBroadcasters(nRow1, nRow2);
+    aCol[nCol].DeleteBroadcasters(rBlockPos, nRow1, nRow2);
 }
 
 const SvtBroadcaster* ScTable::GetBroadcaster( SCCOL nCol, SCROW nRow ) const
commit b009069dd1297bbf148011e777c829abab6b35c5
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 12:35:05 2013 -0400

    A bit of cleanup.
    
    Change-Id: I9f200377c1f5bf44eb4ba60c272b02ae3d632a19

diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index d47f837..d9145b0 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -2229,26 +2229,29 @@ ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler()
 
 SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos )
 {
-    if (!TableExists(rPos.Tab()))
+    ScTable* pTab = FetchTable(rPos.Tab());
+    if (!pTab)
         return NULL;
 
-    return maTabs[rPos.Tab()]->GetBroadcaster(rPos.Col(), rPos.Row());
+    return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
 }
 
 const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const
 {
-    if (!TableExists(rPos.Tab()))
+    ScTable* pTab = FetchTable(rPos.Tab());
+    if (!pTab)
         return NULL;
 
-    return maTabs[rPos.Tab()]->GetBroadcaster(rPos.Col(), rPos.Row());
+    return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
 }
 
 void ScDocument::DeleteBroadcasters( const ScAddress& rTopPos, SCROW nLength )
 {
-    if (!TableExists(rTopPos.Tab()) || nLength <= 0)
+    ScTable* pTab = FetchTable(rTopPos.Tab());
+    if (!pTab || nLength <= 0)
         return;
 
-    maTabs[rTopPos.Tab()]->DeleteBroadcasters(rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
+    pTab->DeleteBroadcasters(rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
 }
 
 bool ScDocument::TableExists( SCTAB nTab ) const
commit ba4ab27b1ef4c767c8e0f576bd2499a28c6b8151
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 12:25:19 2013 -0400

    Do the same trick when getting broadcasters during mass formula cell deletion.
    
    Change-Id: Iee1b3ff637d10c6bd2d2342db518a2fc986327b7

diff --git a/sc/inc/listenercontext.hxx b/sc/inc/listenercontext.hxx
index 2503e72..f4fbb0f 100644
--- a/sc/inc/listenercontext.hxx
+++ b/sc/inc/listenercontext.hxx
@@ -35,10 +35,13 @@ class EndListeningContext : boost::noncopyable
 {
     ScDocument& mrDoc;
     ColumnSpanSet maSet;
+    ColumnBlockPositionSet maPosSet;
 public:
     EndListeningContext(ScDocument& rDoc);
     ScDocument& getDoc();
 
+    ColumnBlockPosition* getBlockPosition(SCTAB nTab, SCCOL nCol);
+
     void addEmptyBroadcasterPosition(SCTAB nTab, SCCOL nCol, SCROW nRow);
     void purgeEmptyBroadcasters();
 };
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index eeb5519..324519a 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -2107,10 +2107,19 @@ void ScColumn::StartListening( sc::StartListeningContext& rCxt, SCROW nRow, SvtL
 
 void ScColumn::EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListener& rListener )
 {
-    SvtBroadcaster* pBC = GetBroadcaster(nRow);
-    if (!pBC)
+    sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
+    if (!p)
+        return;
+
+    sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
+    std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, nRow);
+    it = aPos.first; // store the block position for next iteration.
+    if (it->type != sc::element_type_broadcaster)
         return;
 
+    SvtBroadcaster* pBC = sc::custom_broadcaster_block::at(*it->data, aPos.second);
+    OSL_ASSERT(pBC);
+
     rListener.EndListening(*pBC);
     if (!pBC->HasListeners())
         // There is no more listeners for this cell. Add it to the purge list for later purging.
diff --git a/sc/source/core/data/listenercontext.cxx b/sc/source/core/data/listenercontext.cxx
index 7ab3799..f53f1bc 100644
--- a/sc/source/core/data/listenercontext.cxx
+++ b/sc/source/core/data/listenercontext.cxx
@@ -40,13 +40,18 @@ ColumnBlockPosition* StartListeningContext::getBlockPosition(SCTAB nTab, SCCOL n
     return maSet.getBlockPosition(nTab, nCol);
 }
 
-EndListeningContext::EndListeningContext(ScDocument& rDoc) : mrDoc(rDoc) {}
+EndListeningContext::EndListeningContext(ScDocument& rDoc) : mrDoc(rDoc), maPosSet(rDoc) {}
 
 ScDocument& EndListeningContext::getDoc()
 {
     return mrDoc;
 }
 
+ColumnBlockPosition* EndListeningContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+    return maPosSet.getBlockPosition(nTab, nCol);
+}
+
 void EndListeningContext::addEmptyBroadcasterPosition(SCTAB nTab, SCCOL nCol, SCROW nRow)
 {
     maSet.set(nTab, nCol, nRow, true);
commit e219cd1ea20534a3a86922dd39e28dcc877eb6cd
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Mon May 20 11:12:49 2013 -0400

    Keep track of column block positions when mass-pasting formula cells.
    
    This speeds up the following scenario:
    
    1) type =B1 in A1. Leave A2 empty.
    2) Select A1:A2 and Ctrl-C to copy.
    3) Select A3:A50000 (or longer if you so wish), and ctrl-V to paste.
    
    This causes the broadcaster storage array in column B to be heavily
    partitioned due to the empty cells interspersed between formula cells
    in column A.  Without tracking the column position this would cause a
    O(n^2) complexity algorithm.
    
    Change-Id: Ic2f23c2c2bea3353c517faa73fe5412c7528bd95

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 46ffbd9..6db56d4 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -37,6 +37,7 @@ namespace editeng { class SvxBorderLine; }
 
 namespace sc {
     struct FormulaGroupContext;
+    class StartListeningContext;
     class EndListeningContext;
     class CopyFromClipContext;
     class CopyToClipContext;
@@ -226,7 +227,7 @@ public:
     void CopyFromClip(
         sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, long nDy, ScColumn& rColumn );
 
-    void        StartListeningInArea( SCROW nRow1, SCROW nRow2 );
+    void StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 );
     void        BroadcastInArea( SCROW nRow1, SCROW nRow2 );
 
     void        RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );
@@ -433,6 +434,7 @@ public:
 
     void        StartListening( SvtListener& rLst, SCROW nRow );
     void        EndListening( SvtListener& rLst, SCROW nRow );
+    void StartListening( sc::StartListeningContext& rCxt, SCROW nRow, SvtListener& rListener );
     void EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListener& rListener );
     void        MoveListeners( SvtBroadcaster& rSource, SCROW nDestRow );
     void        StartAllListeners();
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index f01e811..544c9b5 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -49,6 +49,7 @@
 namespace editeng { class SvxBorderLine; }
 namespace sc {
     struct FormulaGroupContext;
+    class StartListeningContext;
     class EndListeningContext;
     class CopyFromClipContext;
     struct ColumnBlockPosition;
@@ -1765,6 +1766,7 @@ public:
     void                EndListeningCell( const ScAddress& rAddress,
                                             SvtListener* pListener );
 
+    void StartListeningCell( sc::StartListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener );
     void EndListeningCell( sc::EndListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener );
 
     void EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells );
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index e081353..4760062 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -30,6 +30,7 @@
 
 namespace sc {
 
+class StartListeningContext;
 class EndListeningContext;
 
 }
@@ -313,6 +314,7 @@ public:
 
     // nOnlyNames may be one or more of SC_LISTENING_NAMES_*
     void StartListeningTo( ScDocument* pDoc );
+    void StartListeningTo( sc::StartListeningContext& rCxt );
     void EndListeningTo(
         ScDocument* pDoc, ScTokenArray* pArr = NULL, ScAddress aPos = ScAddress() );
     void EndListeningTo( sc::EndListeningContext& rCxt );
diff --git a/sc/inc/listenercontext.hxx b/sc/inc/listenercontext.hxx
index 36f26d8..2503e72 100644
--- a/sc/inc/listenercontext.hxx
+++ b/sc/inc/listenercontext.hxx
@@ -12,6 +12,7 @@
 
 #include "address.hxx"
 #include "columnspanset.hxx"
+#include "mtvelements.hxx"
 
 #include <boost/noncopyable.hpp>
 
@@ -19,6 +20,17 @@ class ScDocument;
 
 namespace sc {
 
+class StartListeningContext : boost::noncopyable
+{
+    ScDocument& mrDoc;
+    ColumnBlockPositionSet maSet;
+public:
+    StartListeningContext(ScDocument& rDoc);
+    ScDocument& getDoc();
+
+    ColumnBlockPosition* getBlockPosition(SCTAB nTab, SCCOL nCol);
+};
+
 class EndListeningContext : boost::noncopyable
 {
     ScDocument& mrDoc;
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index f04a9bb..48fca53 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -49,6 +49,7 @@ namespace com { namespace sun { namespace star {
 
 namespace sc {
     struct FormulaGroupContext;
+    class StartListeningContext;
     class EndListeningContext;
     class CopyFromClipContext;
     class CopyToClipContext;
@@ -393,8 +394,9 @@ public:
         sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
         SCsCOL nDx, SCsROW nDy, ScTable* pTable );
 
-    void        StartListeningInArea( SCCOL nCol1, SCROW nRow1,
-                                        SCCOL nCol2, SCROW nRow2 );
+    void StartListeningInArea(
+        sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 );
+
     void        BroadcastInArea( SCCOL nCol1, SCROW nRow1,
                                     SCCOL nCol2, SCROW nRow2 );
 
@@ -948,6 +950,7 @@ private:
 
     void        StartListening( const ScAddress& rAddress, SvtListener* pListener );
     void        EndListening( const ScAddress& rAddress, SvtListener* pListener );
+    void StartListening( sc::StartListeningContext& rCxt, SCCOL nCol, SCROW nRow, SvtListener& rListener );
     void EndListening( sc::EndListeningContext& rCxt, SCCOL nCol, SCROW nRow, SvtListener& rListener );
     void        StartAllListeners();
     void        StartNeededListeners(); // only for cells where NeedsListening()==TRUE
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 196eba1..eeb5519 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -2013,17 +2013,18 @@ void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, bool* pUsed ) const
     }
 }
 
-void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
+namespace {
+
+void startListening(
+    sc::BroadcasterStoreType& rStore, sc::BroadcasterStoreType::iterator& itBlockPos, size_t nElemPos,
+    SCROW nRow, SvtListener& rLst)
 {
-    std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
-    sc::BroadcasterStoreType::iterator it = aPos.first; // block position.
-    size_t nElemPos = aPos.second; // element position within the block.
-    switch (it->type)
+    switch (itBlockPos->type)
     {
         case sc::element_type_broadcaster:
         {
             // Broadcaster already exists here.
-            SvtBroadcaster* pBC = sc::custom_broadcaster_block::at(*it->data, nElemPos);
+            SvtBroadcaster* pBC = sc::custom_broadcaster_block::at(*itBlockPos->data, nElemPos);
             rLst.StartListening(*pBC);
         }
         break;
@@ -2032,7 +2033,7 @@ void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
             // No broadcaster exists at this position yet.
             SvtBroadcaster* pBC = new SvtBroadcaster;
             rLst.StartListening(*pBC);
-            maBroadcasters.set(it, nRow, pBC);
+            itBlockPos = rStore.set(itBlockPos, nRow, pBC); // Store the block position for next iteration.
         }
         break;
         default:
@@ -2046,6 +2047,14 @@ void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
     }
 }
 
+}
+
+void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
+{
+    std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
+    startListening(maBroadcasters, aPos.first, aPos.second, nRow, rLst);
+}
+
 void ScColumn::MoveListeners( SvtBroadcaster& rSource, SCROW nDestRow )
 {
     // Move listeners from the source position to the destination position.
@@ -2081,6 +2090,21 @@ void ScColumn::EndListening( SvtListener& rLst, SCROW nRow )
         maBroadcasters.set_empty(nRow, nRow);
 }
 
+void ScColumn::StartListening( sc::StartListeningContext& rCxt, SCROW nRow, SvtListener& rLst )
+{
+    if (!ValidRow(nRow))
+        return;
+
+    sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
+    if (!p)
+        return;
+
+    sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
+    std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, nRow);
+    it = aPos.first; // store the block position for next iteration.
+    startListening(maBroadcasters, it, aPos.second, nRow, rLst);
+}
+
 void ScColumn::EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListener& rListener )
 {
     SvtBroadcaster* pBC = GetBroadcaster(nRow);
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 70d007d..60eb8e3 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1166,7 +1166,7 @@ void ScColumn::BroadcastInArea( SCROW nRow1, SCROW nRow2 )
 }
 
 
-void ScColumn::StartListeningInArea( SCROW nRow1, SCROW nRow2 )
+void ScColumn::StartListeningInArea( sc::StartListeningContext& rCxt, SCROW nRow1, SCROW nRow2 )
 {
     if (maItems.empty())
         return;
@@ -1178,7 +1178,7 @@ void ScColumn::StartListeningInArea( SCROW nRow1, SCROW nRow2 )
     {
         ScBaseCell* pCell = maItems[nIndex].pCell;
         if ( pCell->GetCellType() == CELLTYPE_FORMULA )
-            ((ScFormulaCell*)pCell)->StartListeningTo( pDocument );
+            ((ScFormulaCell*)pCell)->StartListeningTo(rCxt);
         if ( nRow != maItems[nIndex].nRow )
             Search( nRow, nIndex ); // Inserted via Listening
 
diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx
index b240633..1c3f665 100644
--- a/sc/source/core/data/documen7.cxx
+++ b/sc/source/core/data/documen7.cxx
@@ -201,13 +201,24 @@ void ScDocument::EndListeningCell( const ScAddress& rAddress,
         maTabs[nTab]->EndListening( rAddress, pListener );
 }
 
+void ScDocument::StartListeningCell(
+    sc::StartListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
+{
+    ScTable* pTab = FetchTable(rPos.Tab());
+    if (!pTab)
+        return;
+
+    pTab->StartListening(rCxt, rPos.Col(), rPos.Row(), rListener);
+}
+
 void ScDocument::EndListeningCell(
     sc::EndListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
 {
-    if (!TableExists(rPos.Tab()))
+    ScTable* pTab = FetchTable(rPos.Tab());
+    if (!pTab)
         return;
 
-    maTabs[rPos.Tab()]->EndListening(rCxt, rPos.Col(), rPos.Row(), rListener);
+    pTab->EndListening(rCxt, rPos.Col(), rPos.Row(), rListener);
 }
 
 void ScDocument::EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells )
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 54b7414..d47f837 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -91,6 +91,7 @@
 #include "formulaiter.hxx"
 #include "formulacell.hxx"
 #include "clipcontext.hxx"
+#include "listenercontext.hxx"
 
 #include <map>
 #include <limits>
@@ -2311,11 +2312,12 @@ void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
 {
     if (nInsFlag & IDF_CONTENTS)
     {
+        sc::StartListeningContext aCxt(*this);
         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( nCol1, nRow1, nCol2, nRow2 );
+                maTabs[*itr]->StartListeningInArea(aCxt, nCol1, nRow1, nCol2, nRow2);
     }
 }
 
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 30810df..98df69a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3162,6 +3162,34 @@ bool ScFormulaCell::InterpretInvariantFormulaGroup()
     return true;
 }
 
+namespace {
+
+void startListeningArea(
+    ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const ScToken& rToken)
+{
+    const ScSingleRefData& rRef1 = rToken.GetSingleRef();
+    const ScSingleRefData& rRef2 = rToken.GetSingleRef2();
+    ScAddress aCell1 = rRef1.toAbs(rPos);
+    ScAddress aCell2 = rRef2.toAbs(rPos);
+    if (aCell1.IsValid() && aCell2.IsValid())
+    {
+        if (rToken.GetOpCode() == ocColRowNameAuto)
+        {   // automagically
+            if ( rRef1.IsColRel() )
+            {   // ColName
+                aCell2.SetRow(MAXROW);
+            }
+            else
+            {   // RowName
+                aCell2.SetCol(MAXCOL);
+            }
+        }
+        rDoc.StartListeningArea(ScRange(aCell1, aCell2), pCell);
+    }
+}
+
+}
+
 void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
 {
     if (pDoc->IsClipOrUndo() || pDoc->GetNoListening() || IsInChangeTrack())
@@ -3181,40 +3209,58 @@ void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
     ScToken* t;
     while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
     {
-        StackVar eType = t->GetType();
-        ScSingleRefData& rRef1 = t->GetSingleRef();
-        ScSingleRefData& rRef2 = (eType == svDoubleRef ?
-            t->GetDoubleRef().Ref2 : rRef1);
-        switch( eType )
+        switch (t->GetType())
         {
             case svSingleRef:
             {
-                ScAddress aCell = rRef1.toAbs(aPos);
+                ScAddress aCell =  t->GetSingleRef().toAbs(aPos);
                 if (aCell.IsValid())
                     pDoc->StartListeningCell(aCell, this);
             }
             break;
             case svDoubleRef:
+                startListeningArea(this, *pDoc, aPos, *t);
+            break;
+            default:
+                ;   // nothing
+        }
+    }
+    SetNeedsListening( false);
+}
+
+void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt )
+{
+    ScDocument& rDoc = rCxt.getDoc();
+
+    if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack())
+        return;
+
+    rDoc.SetDetectiveDirty(true);  // It has changed something
+
+    ScTokenArray* pArr = GetCode();
+    if( pArr->IsRecalcModeAlways() )
+    {
+        rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, this);
+        SetNeedsListening( false);
+        return;
+    }
+
+    pArr->Reset();
+    ScToken* t;
+    while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
+    {
+        switch (t->GetType())
+        {
+            case svSingleRef:
             {
-                ScAddress aCell1 = rRef1.toAbs(aPos);
-                ScAddress aCell2 = rRef2.toAbs(aPos);
-                if (aCell1.IsValid() && aCell2.IsValid())
-                {
-                    if (t->GetOpCode() == ocColRowNameAuto)
-                    {   // automagically
-                        if ( rRef1.IsColRel() )
-                        {   // ColName
-                            aCell2.SetRow(MAXROW);
-                        }
-                        else
-                        {   // RowName
-                            aCell2.SetCol(MAXCOL);
-                        }
-                    }
-                    pDoc->StartListeningArea(ScRange(aCell1, aCell2), this);
-                }
+                ScAddress aCell = t->GetSingleRef().toAbs(aPos);
+                if (aCell.IsValid())
+                    rDoc.StartListeningCell(rCxt, aCell, *this);
             }
             break;
+            case svDoubleRef:
+                startListeningArea(this, rDoc, aPos, *t);
+            break;
             default:
                 ;   // nothing
         }
diff --git a/sc/source/core/data/listenercontext.cxx b/sc/source/core/data/listenercontext.cxx
index a288494..7ab3799 100644
--- a/sc/source/core/data/listenercontext.cxx
+++ b/sc/source/core/data/listenercontext.cxx
@@ -28,6 +28,18 @@ public:
 
 }
 
+StartListeningContext::StartListeningContext(ScDocument& rDoc) : mrDoc(rDoc), maSet(rDoc) {}
+
+ScDocument& StartListeningContext::getDoc()
+{
+    return mrDoc;
+}
+
+ColumnBlockPosition* StartListeningContext::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+    return maSet.getBlockPosition(nTab, nCol);
+}
+
 EndListeningContext::EndListeningContext(ScDocument& rDoc) : mrDoc(rDoc) {}
 
 ScDocument& EndListeningContext::getDoc()
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 51801af..0f5d458 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -1010,14 +1010,14 @@ void ScTable::BroadcastInArea( SCCOL nCol1, SCROW nRow1,
 }
 
 
-void ScTable::StartListeningInArea( SCCOL nCol1, SCROW nRow1,
-        SCCOL nCol2, SCROW nRow2 )
+void ScTable::StartListeningInArea(
+    sc::StartListeningContext& rCxt, 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( nRow1, nRow2 );
+            aCol[i].StartListeningInArea(rCxt, nRow1, nRow2);
 }
 
 
diff --git a/sc/source/core/data/table5.cxx b/sc/source/core/data/table5.cxx
index 9468352..ed15086 100644
--- a/sc/source/core/data/table5.cxx
+++ b/sc/source/core/data/table5.cxx
@@ -1097,6 +1097,14 @@ void ScTable::EndListening( const ScAddress& rAddress, SvtListener* pListener )
     aCol[rAddress.Col()].EndListening( *pListener, rAddress.Row() );
 }
 
+void ScTable::StartListening( sc::StartListeningContext& rCxt, SCCOL nCol, SCROW nRow, SvtListener& rListener )
+{
+    if (!ValidCol(nCol))
+        return;
+
+    aCol[nCol].StartListening(rCxt, nRow, rListener);
+}
+
 void ScTable::EndListening( sc::EndListeningContext& rCxt, SCCOL nCol, SCROW nRow, SvtListener& rListener )
 {
     if (!ValidCol(nCol))
commit 82a0740ca9324088580814e4558abadacfe6223a
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 23:33:30 2013 -0400

    A bit of cleanup before refactoring.
    
    Change-Id: I3627a83669b6a69c299aef96b8b2ead1352eabe2

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index b243ea2..30810df 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3188,53 +3188,32 @@ void ScFormulaCell::StartListeningTo( ScDocument* pDoc )
         switch( eType )
         {
             case svSingleRef:
-                rRef1.CalcAbsIfRel(aPos);
-                if ( rRef1.Valid() )
-                {
-                    pDoc->StartListeningCell(
-                        ScAddress( rRef1.nCol,
-                                   rRef1.nRow,
-                                   rRef1.nTab ), this );
-                }
+            {
+                ScAddress aCell = rRef1.toAbs(aPos);
+                if (aCell.IsValid())
+                    pDoc->StartListeningCell(aCell, this);
+            }
             break;
             case svDoubleRef:
-                t->CalcAbsIfRel(aPos);
-                if ( rRef1.Valid() && rRef2.Valid() )
+            {
+                ScAddress aCell1 = rRef1.toAbs(aPos);
+                ScAddress aCell2 = rRef2.toAbs(aPos);
+                if (aCell1.IsValid() && aCell2.IsValid())
                 {
-                    if ( t->GetOpCode() == ocColRowNameAuto )
+                    if (t->GetOpCode() == ocColRowNameAuto)
                     {   // automagically
                         if ( rRef1.IsColRel() )
                         {   // ColName
-                            pDoc->StartListeningArea( ScRange (
-                                rRef1.nCol,
-                                rRef1.nRow,
-                                rRef1.nTab,
-                                rRef2.nCol,
-                                MAXROW,
-                                rRef2.nTab ), this );
+                            aCell2.SetRow(MAXROW);
                         }
                         else
                         {   // RowName
-                            pDoc->StartListeningArea( ScRange (
-                                rRef1.nCol,
-                                rRef1.nRow,
-                                rRef1.nTab,
-                                MAXCOL,
-                                rRef2.nRow,
-                                rRef2.nTab ), this );
+                            aCell2.SetCol(MAXCOL);
                         }
                     }
-                    else
-                    {
-                        pDoc->StartListeningArea( ScRange (
-                            rRef1.nCol,
-                            rRef1.nRow,
-                            rRef1.nTab,
-                            rRef2.nCol,
-                            rRef2.nRow,
-                            rRef2.nTab ), this );
-                    }
+                    pDoc->StartListeningArea(ScRange(aCell1, aCell2), this);
                 }
+            }
             break;
             default:
                 ;   // nothing
commit d82ea27a46fb7fb9bff7a633a9f3261e40d171a0
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 22:45:43 2013 -0400

    Prefer early bail-out.
    
    Change-Id: I112a4be56910c07007b28715336fcd82d56bb313

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index f573011..70d007d 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1103,38 +1103,40 @@ ScAttrIterator* ScColumn::CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) c
 
 void ScColumn::StartAllListeners()
 {
-    if ( !maItems.empty() )
-        for (SCSIZE i = 0; i < maItems.size(); i++)
+    if (maItems.empty())
+        return;
+
+    for (SCSIZE i = 0; i < maItems.size(); i++)
+    {
+        ScBaseCell* pCell = maItems[i].pCell;
+        if ( pCell->GetCellType() == CELLTYPE_FORMULA )
         {
-            ScBaseCell* pCell = maItems[i].pCell;
-            if ( pCell->GetCellType() == CELLTYPE_FORMULA )
-            {
-                SCROW nRow = maItems[i].nRow;
-                ((ScFormulaCell*)pCell)->StartListeningTo( pDocument );
-                if ( nRow != maItems[i].nRow )
-                    Search( nRow, i ); // Insert Listener?
-            }
+            SCROW nRow = maItems[i].nRow;
+            ((ScFormulaCell*)pCell)->StartListeningTo( pDocument );
+            if ( nRow != maItems[i].nRow )
+                Search( nRow, i ); // Insert Listener?
         }
+    }
 }
 
 
 void ScColumn::StartNeededListeners()
 {
-    if ( !maItems.empty() )
+    if (maItems.empty())
+        return;
+
+    for (SCSIZE i = 0; i < maItems.size(); i++)
     {
-        for (SCSIZE i = 0; i < maItems.size(); i++)
+        ScBaseCell* pCell = maItems[i].pCell;
+        if ( pCell->GetCellType() == CELLTYPE_FORMULA )
         {
-            ScBaseCell* pCell = maItems[i].pCell;
-            if ( pCell->GetCellType() == CELLTYPE_FORMULA )
+            ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
+            if (pFCell->NeedsListening())
             {
-                ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell);
-                if (pFCell->NeedsListening())
-                {
-                    SCROW nRow = maItems[i].nRow;
-                    pFCell->StartListeningTo( pDocument );
-                    if ( nRow != maItems[i].nRow )
-                        Search( nRow, i ); // Insert Listener?
-                }
+                SCROW nRow = maItems[i].nRow;
+                pFCell->StartListeningTo( pDocument );
+                if ( nRow != maItems[i].nRow )
+                    Search( nRow, i ); // Insert Listener?
             }
         }
     }
@@ -1166,20 +1168,21 @@ void ScColumn::BroadcastInArea( SCROW nRow1, SCROW nRow2 )
 
 void ScColumn::StartListeningInArea( SCROW nRow1, SCROW nRow2 )
 {
-    if ( !maItems.empty() )
+    if (maItems.empty())
+        return;
+
+    SCROW nRow;
+    SCSIZE nIndex;
+    Search( nRow1, nIndex );
+    while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRow2 )
     {
-        SCROW nRow;
-        SCSIZE nIndex;
-        Search( nRow1, nIndex );
-        while ( nIndex < maItems.size() && (nRow = maItems[nIndex].nRow) <= nRow2 )
-        {
-            ScBaseCell* pCell = maItems[nIndex].pCell;
-            if ( pCell->GetCellType() == CELLTYPE_FORMULA )
-                ((ScFormulaCell*)pCell)->StartListeningTo( pDocument );
-            if ( nRow != maItems[nIndex].nRow )
-                Search( nRow, nIndex ); // Inserted via Listening
-            nIndex++;
-        }
+        ScBaseCell* pCell = maItems[nIndex].pCell;
+        if ( pCell->GetCellType() == CELLTYPE_FORMULA )
+            ((ScFormulaCell*)pCell)->StartListeningTo( pDocument );
+        if ( nRow != maItems[nIndex].nRow )
+            Search( nRow, nIndex ); // Inserted via Listening
+
+        ++nIndex;
     }
 }
 
commit 950143071b74fbd78df0ec45864143a3ca741a01
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 22:16:45 2013 -0400

    Compiler warning.
    
    Change-Id: I6f358f89348a23b3c70f8b7db8a1f01d659393ba

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 9339e8b..f573011 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -906,8 +906,7 @@ void ScColumn::MixData(
     SCSIZE nIndex;
     Search( nRow1, nIndex );
 
-//  SCSIZE nSrcIndex = 0;
-    SCSIZE nSrcIndex, nDestIndex;
+    SCSIZE nSrcIndex = 0, nDestIndex = 0;
     rSrcCol.Search( nRow1, nSrcIndex ); // See if data is at the beginning
 
     SCROW nNextThis = MAXROW+1;
commit 126d52dda33ddd6a9960bbd4d7b8bfafe9fa2107
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 21:23:22 2013 -0400

    Optimize ScColumn::MixData() to remove several bottlenecks.
    
    The use case that would previously make Calc freeze.
    
    1) type in "test" in A1.
    2) Select A1:A2 and Ctrl-C to copy to the clipboard.
    3) select the whole column B.
    4) paste special, select "Add" operation and click OK to paste.
    
    With this change, the above scenario no longer freezes Calc.
    
    Change-Id: I98b1f1e6c25b853aa884e7598c04b1457fab4636

diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index c71ffea..46ffbd9 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -497,6 +497,12 @@ private:
     void SetCell( sc::ColumnBlockPosition& rBlockPos, SCROW nRow, ScBaseCell* pNewCell );
     void SetCell( SCROW nRow, ScBaseCell* pNewCell );
     void PostSetCell( SCROW nRow, ScBaseCell* pNewCell );
+
+    /**
+     * Clear and re-populate the cell text attribute array from the non-empty
+     * cells stored in the cell array.
+     */
+    void ResetCellTextAttrs();
 };
 
 
diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx
index 05430f3..196eba1 100644
--- a/sc/source/core/data/column2.cxx
+++ b/sc/source/core/data/column2.cxx
@@ -1569,6 +1569,32 @@ void ScColumn::PostSetCell( SCROW nRow, ScBaseCell* pNewCell )
     }
 }
 
+namespace {
+
+class SetEmptyAttr : std::unary_function<ColEntry, void>
+{
+    sc::CellTextAttrStoreType& mrAttrStore;
+    sc::CellTextAttrStoreType::iterator miPos;
+public:
+    SetEmptyAttr(sc::CellTextAttrStoreType& rAttrStore) :
+        mrAttrStore(rAttrStore), miPos(rAttrStore.begin()) {}
+
+    void operator() (const ColEntry& rEntry)
+    {
+        miPos = mrAttrStore.set(miPos, rEntry.nRow, sc::CellTextAttr());
+    }
+};
+
+}
+
+void ScColumn::ResetCellTextAttrs()
+{
+    maCellTextAttrs.clear();
+    maCellTextAttrs.resize(MAXROWCOUNT);
+
+    std::for_each(maItems.begin(), maItems.end(), SetEmptyAttr(maCellTextAttrs));
+}
+
 SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow)
 {
     return maBroadcasters.get<SvtBroadcaster*>(nRow);
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index b530184..9339e8b 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -842,11 +842,12 @@ void ScColumn::MixMarked(
     }
 }
 
+namespace {
 
 // Result in rVal1
-static sal_Bool lcl_DoFunction( double& rVal1, double nVal2, sal_uInt16 nFunction )
+bool lcl_DoFunction( double& rVal1, double nVal2, sal_uInt16 nFunction )
 {
-    sal_Bool bOk = false;
+    bool bOk = false;
     switch (nFunction)
     {
         case PASTE_ADD:
@@ -866,8 +867,7 @@ static sal_Bool lcl_DoFunction( double& rVal1, double nVal2, sal_uInt16 nFunctio
     return bOk;
 }
 
-
-static void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell )
+void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell )
 {
     rArr.AddOpCode(ocOpen);
 
@@ -885,6 +885,15 @@ static void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell )
     rArr.AddOpCode(ocClose);
 }
 
+struct FindRemovedCell : std::unary_function<ColEntry, bool>
+{
+    bool operator() (const ColEntry& rEntry) const
+    {
+        return rEntry.pCell == NULL;
+    }
+};
+
+}
 
 void ScColumn::MixData(
     sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction,
@@ -892,11 +901,13 @@ void ScColumn::MixData(
 {
     SCSIZE nSrcCount = rSrcCol.maItems.size();
 
+    sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
+
     SCSIZE nIndex;
     Search( nRow1, nIndex );
 
 //  SCSIZE nSrcIndex = 0;
-    SCSIZE nSrcIndex;
+    SCSIZE nSrcIndex, nDestIndex;
     rSrcCol.Search( nRow1, nSrcIndex ); // See if data is at the beginning
 
     SCROW nNextThis = MAXROW+1;
@@ -906,6 +917,7 @@ void ScColumn::MixData(
     if ( nSrcIndex < nSrcCount )
         nNextSrc = rSrcCol.maItems[nSrcIndex].nRow;
 
+    bool bDeferredDelete = false;
     while ( nNextThis <= nRow2 || nNextSrc <= nRow2 )
     {
         SCROW nRow = std::min( nNextThis, nNextSrc );
@@ -913,13 +925,16 @@ void ScColumn::MixData(
         ScBaseCell* pSrc = NULL;
         ScBaseCell* pDest = NULL;
         ScBaseCell* pNew = NULL;
-        sal_Bool bDelete = false;
+        bool bDelete = false;
 
         if ( nSrcIndex < nSrcCount && nNextSrc == nRow )
             pSrc = rSrcCol.maItems[nSrcIndex].pCell;
 
         if ( nIndex < maItems.size() && nNextThis == nRow )
+        {
             pDest = maItems[nIndex].pCell;
+            nDestIndex = nIndex;
+        }
 
         OSL_ENSURE( pSrc || pDest, "What happened?" );
 
@@ -1024,10 +1039,18 @@ void ScColumn::MixData(
 
         if ( pNew || bDelete ) // New result?
         {
-            sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
             if (pDest && !pNew) // Old cell present?
             {
-                Delete(nRow); // -> Delete
+                // Delete the destination cell because the cell was originally
+                // empty.  Don't erase its slot in the cell array yet.
+                OSL_ASSERT(pDest == maItems[nDestIndex].pCell);
+                maItems[nDestIndex].pCell = NULL;
+
+                if (pDest->GetCellType() == CELLTYPE_FORMULA)
+                    static_cast<ScFormulaCell*>(pDest)->EndListeningTo(pDocument);
+                pDest->Delete();
+
+                bDeferredDelete = true;
             }
             if (pNew)
             {
@@ -1057,6 +1080,19 @@ void ScColumn::MixData(
                             MAXROW+1;
         }
     }
+
+    if (bDeferredDelete)
+    {
+        // Erase all the slots in the cell array where the deleted cells
+        // previously occupied.
+        std::vector<ColEntry>::iterator it =
+            std::remove_if(maItems.begin(), maItems.end(), FindRemovedCell());
+
+        maItems.erase(it, maItems.end());
+
+        // Reset the cell text attriute array to keep it in sync again.
+        ResetCellTextAttrs();
+    }
 }
 
 
diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx
index ced0936..ec5b1c4 100644
--- a/sc/source/core/data/documentimport.cxx
+++ b/sc/source/core/data/documentimport.cxx
@@ -118,14 +118,7 @@ void ScDocumentImport::finalize()
         for (; pCol != pColEnd; ++pCol)
         {
             ScColumn& rCol = *pCol;
-            if (rCol.maItems.empty())
-                // Column has no cells. Skip it.
-                continue;
-
-            sc::CellTextAttrStoreType::iterator itCTAPos = rCol.maCellTextAttrs.begin();
-            std::vector<ColEntry>::iterator itCell = rCol.maItems.begin(), itCellEnd = rCol.maItems.end();
-            for (; itCell != itCellEnd; ++itCell)
-                itCTAPos = rCol.maCellTextAttrs.set<sc::CellTextAttr>(itCTAPos, itCell->nRow, sc::CellTextAttr());
+            rCol.ResetCellTextAttrs();
         }
     }
 }
commit 3d09c7e5eebc3cf3543adef125f04aaaa1c74b91
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 16:48:25 2013 -0400

    A bit of a cleanup.
    
    Change-Id: Iaa7003cabee4630e4fbb8fb1cd114d04b4e9e33c

diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index 599da6e..84b1fcc 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -22,13 +22,7 @@ namespace sc {
 
 class ClipContextBase
 {
-    typedef boost::unordered_map<SCCOL, ColumnBlockPosition> ColumnsType;
-    typedef std::vector<ColumnsType> TablesType;
-
-    ScDocument& mrDoc;
-    TablesType maTables;
-    SCTAB mnTabStart;
-    SCTAB mnTabEnd;
+    sc::ColumnBlockPositionSet maSet;
 
     ClipContextBase(); // disabled
 
@@ -36,16 +30,13 @@ public:
     ClipContextBase(ScDocument& rDoc);
     virtual ~ClipContextBase();
 
-    void setTabRange(SCTAB nStart, SCTAB nEnd);
-
-    SCTAB getTabStart() const;
-    SCTAB getTabEnd() const;
-
     ColumnBlockPosition* getBlockPosition(SCTAB nTab, SCCOL nCol);
 };
 
 class CopyFromClipContext : public ClipContextBase
 {
+    SCTAB mnTabStart;
+    SCTAB mnTabEnd;
     ScDocument* mpRefUndoDoc;
     ScDocument* mpClipDoc;
     sal_uInt16  mnInsertFlag;
@@ -61,6 +52,11 @@ public:
 
     virtual ~CopyFromClipContext();
 
+    void setTabRange(SCTAB nStart, SCTAB nEnd);
+
+    SCTAB getTabStart() const;
+    SCTAB getTabEnd() const;
+
     ScDocument* getUndoDoc();
     ScDocument* getClipDoc();
     sal_uInt16 getInsertFlag() const;
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
index 2c7abdf..2b51076 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -13,62 +13,20 @@
 namespace sc {
 
 ClipContextBase::ClipContextBase(ScDocument& rDoc) :
-    mrDoc(rDoc), mnTabStart(-1), mnTabEnd(-1) {}
+    maSet(rDoc) {}
 
 ClipContextBase::~ClipContextBase() {}
 
-void ClipContextBase::setTabRange(SCTAB nStart, SCTAB nEnd)
-{
-    mnTabStart = nStart;
-    mnTabEnd = nEnd;
-}
-
-SCTAB ClipContextBase::getTabStart() const
-{
-    return mnTabStart;
-}
-
-SCTAB ClipContextBase::getTabEnd() const
-{
-    return mnTabEnd;
-}
-
 ColumnBlockPosition* ClipContextBase::getBlockPosition(SCTAB nTab, SCCOL nCol)
 {
-    if (mnTabStart < 0 || mnTabEnd < 0 || mnTabStart > mnTabEnd)
-        return NULL;
-
-    size_t nTabIndex = nTab - mnTabStart;
-    if (nTabIndex >= maTables.size())
-        maTables.resize(nTabIndex+1);
-
-    ColumnsType& rCols = maTables[nTabIndex];
-
-    ColumnsType::iterator it = rCols.find(nCol);
-    if (it != rCols.end())
-        // Block position for this column has already been fetched.
-        return &it->second;
-
-    std::pair<ColumnsType::iterator,bool> r =
-        rCols.insert(
-            ColumnsType::value_type(nCol, ColumnBlockPosition()));
-
-    if (!r.second)
-        // insertion failed.
-        return NULL;
-
-    it = r.first;
-
-    if (!mrDoc.InitColumnBlockPosition(it->second, nTab, nCol))
-        return NULL;
-
-    return &it->second;
+    return maSet.getBlockPosition(nTab, nCol);
 }
 
 CopyFromClipContext::CopyFromClipContext(ScDocument& rDoc,
     ScDocument* pRefUndoDoc, ScDocument* pClipDoc, sal_uInt16 nInsertFlag,
     bool bAsLink, bool bSkipAttrForEmptyCells) :
     ClipContextBase(rDoc),
+    mnTabStart(-1), mnTabEnd(-1),
     mpRefUndoDoc(pRefUndoDoc), mpClipDoc(pClipDoc), mnInsertFlag(nInsertFlag),
     mbAsLink(bAsLink), mbSkipAttrForEmptyCells(bSkipAttrForEmptyCells) {}
 
@@ -76,6 +34,22 @@ CopyFromClipContext::~CopyFromClipContext()
 {
 }
 
+void CopyFromClipContext::setTabRange(SCTAB nStart, SCTAB nEnd)
+{
+    mnTabStart = nStart;
+    mnTabEnd = nEnd;
+}
+
+SCTAB CopyFromClipContext::getTabStart() const
+{
+    return mnTabStart;
+}
+
+SCTAB CopyFromClipContext::getTabEnd() const
+{
+    return mnTabEnd;
+}
+
 ScDocument* CopyFromClipContext::getUndoDoc()
 {
     return mpRefUndoDoc;
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
index 94cd257..10d36b6 100644
--- a/sc/source/core/data/column.cxx
+++ b/sc/source/core/data/column.cxx
@@ -1514,7 +1514,6 @@ void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
 {
     //  This is the scenario table, the data is copied into it
     sc::CopyToDocContext aCxt(*pDocument);
-    aCxt.setTabRange(nTab, nTab);
     ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
     SCROW nStart = -1, nEnd = -1;
     const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
@@ -1546,7 +1545,6 @@ void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
 {
     //  This is the scenario table, the data is copied to the other
     sc::CopyToDocContext aCxt(*rDestCol.pDocument);
-    aCxt.setTabRange(rDestCol.nTab, rDestCol.nTab);
     ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
     SCROW nStart = -1, nEnd = -1;
     const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx
index 0229c52..dcdae98 100644
--- a/sc/source/core/data/documen2.cxx
+++ b/sc/source/core/data/documen2.cxx
@@ -857,7 +857,6 @@ bool ScDocument::CopyTab( SCTAB nOldPos, SCTAB nNewPos, const ScMarkData* pOnlyM
     if (bValid)
     {
         sc::CopyToDocContext aCxt(*this);
-        aCxt.setTabRange(nNewPos, nNewPos);
         SetNoListening( true );     // noch nicht bei CopyToTable/Insert
         maTabs[nOldPos]->CopyToTable(aCxt, 0, 0, MAXCOL, MAXROW, IDF_ALL, (pOnlyMarked != NULL),
                                         maTabs[nNewPos], pOnlyMarked );
@@ -965,7 +964,6 @@ sal_uLong ScDocument::TransferTab( ScDocument* pSrcDoc, SCTAB nSrcPos,
             NumFmtMergeHandler aNumFmtMergeHdl(this, pSrcDoc);
 
             sc::CopyToDocContext aCxt(*this);
-            aCxt.setTabRange(nDestPos, nDestPos);
             nDestPos = std::min(nDestPos, (SCTAB)(GetTableCount() - 1));
             {   // scope for bulk broadcast
                 ScBulkBroadcast aBulkBroadcast( pBASM);
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index decaae9..54b7414 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -1843,7 +1843,6 @@ void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
     if (ValidTab(nTab1) && ValidTab(nTab2))
     {
         sc::CopyToDocContext aCxt(*pDestDoc);
-        aCxt.setTabRange(nTab1, nTab2);
         bool bOldAutoCalc = pDestDoc->GetAutoCalc();
         pDestDoc->SetAutoCalc( false );     // avoid multiple calculations
         for (SCTAB i = nTab1; i <= nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
@@ -1874,7 +1873,6 @@ void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
             CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTab1-1, IDF_FORMULA, false, pDestDoc, pMarks );
 
         sc::CopyToDocContext aCxt(*pDestDoc);
-        aCxt.setTabRange(nTab1, nTab2);
         OSL_ASSERT( nTab2 < static_cast<SCTAB>(maTabs.size()) && nTab2 < static_cast<SCTAB>(pDestDoc->maTabs.size()));
         for (SCTAB i = nTab1; i <= nTab2; i++)
         {
@@ -1903,7 +1901,6 @@ void ScDocument::CopyToDocument(const ScRange& rRange,
     pDestDoc->SetAutoCalc( false );     // avoid multiple calculations
 
     sc::CopyToDocContext aCxt(*pDestDoc);
-    aCxt.setTabRange(aNewRange.aStart.Tab(), aNewRange.aEnd.Tab());
     for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < static_cast<SCTAB>(maTabs.size()); i++)
     {
         if (!TableExists(i) || !pDestDoc->TableExists(i))
@@ -1930,7 +1927,6 @@ void ScDocument::UndoToDocument(const ScRange& rRange,
     bool bOldAutoCalc = pDestDoc->GetAutoCalc();
     pDestDoc->SetAutoCalc( false );     // avoid multiple calculations
     sc::CopyToDocContext aCxt(*pDestDoc);
-    aCxt.setTabRange(nTab1, nTab2);
     if (nTab1 > 0)
         CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTab1-1, IDF_FORMULA, false, pDestDoc, pMarks );
 
@@ -2007,7 +2003,6 @@ void ScDocument::CopyToClip(const ScClipParam& rClipParam,
         pClipDoc->ResetClip(this, pMarks);
 
     sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags, bCloneNoteCaptions);
-    aCxt.setTabRange(i, nEndTab-1);
     CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks, bAllTabs);
 
     for ( ; i < nEndTab; ++i)
@@ -2105,7 +2100,6 @@ void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1,
         pClipDoc->ResetClip( this, nTab );
 
         sc::CopyToClipContext aCxt(*pClipDoc, false, true);
-        aCxt.setTabRange(nTab, nTab);
         if (nTab < static_cast<SCTAB>(maTabs.size()) && nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()))
             if (maTabs[nTab] && pClipDoc->maTabs[nTab])
                 maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab]);
@@ -2863,7 +2857,6 @@ void ScDocument::MixDocument( const ScRange& rRange, sal_uInt16 nFunction, bool
     SCTAB nTab1 = rRange.aStart.Tab();
     SCTAB nTab2 = rRange.aEnd.Tab();
     sc::MixDocContext aCxt(*this);
-    aCxt.setTabRange(nTab1, nTab2);
     for (SCTAB i = nTab1; i <= nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
     {
         ScTable* pTab = FetchTable(i);
@@ -2901,9 +2894,7 @@ void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
         SetAutoCalc( false );                   // avoid multiple calculations
 
         sc::CopyToDocContext aCxt(*this);
-        aCxt.setTabRange(rMark.GetFirstSelected(), rMark.GetLastSelected());
         sc::MixDocContext aMixDocCxt(*this);
-        aMixDocCxt.setTabRange(rMark.GetFirstSelected(), rMark.GetLastSelected());
 
         SCTAB nCount = static_cast<SCTAB>(maTabs.size());
         ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
@@ -2923,7 +2914,6 @@ void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
 
                     // context used for copying content to the temporary mix document.
                     sc::CopyToDocContext aMixCxt(*pMixDoc);
-                    aMixCxt.setTabRange(i, i);
                     maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
                                             IDF_CONTENTS, false, pMixDoc->maTabs[i] );
                 }
@@ -2969,9 +2959,7 @@ void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
         SCROW nEndRow = aArea.aEnd.Row();
 
         sc::CopyToDocContext aCxt(*this);
-        aCxt.setTabRange(rMark.GetFirstSelected(), rMark.GetLastSelected());
         sc::MixDocContext aMixDocCxt(*this);
-        aMixDocCxt.setTabRange(rMark.GetFirstSelected(), rMark.GetLastSelected());
         SCTAB nCount = static_cast<SCTAB>(maTabs.size());
         ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end();
         for (; itr != itrEnd && *itr < nCount; ++itr)
@@ -2989,7 +2977,6 @@ void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
                         pMixDoc->AddUndoTab( i, i );
 
                     sc::CopyToDocContext aMixCxt(*pMixDoc);
-                    aMixCxt.setTabRange(i, i);
                     maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
                                             IDF_CONTENTS, true, pMixDoc->maTabs[i], &rMark );
                 }
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 4af76be..51801af 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -407,7 +407,6 @@ void ScTable::InsertCol( SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE
         nWhichArray[2] = 0;
 
         sc::CopyToDocContext aCxt(*pDocument);
-        aCxt.setTabRange(nTab, nTab);
         for (SCSIZE i=0; i<nSize; i++)
         {
             aCol[nStartCol-1].CopyToColumn(aCxt, nStartRow, nEndRow, IDF_ATTRIB,
commit 259c17fc25e417472e5f3b30de023f4399579e3b
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 16:39:15 2013 -0400

    Keep track of column block positions in ScDocumentImport too.
    
    This currently only affects document import via orcus, but it'll be
    good to put this in place.
    
    Change-Id: I8cc6d54aba6fab1f2774127f92c2a764f7b690fb

diff --git a/sc/inc/documentimport.hxx b/sc/inc/documentimport.hxx
index eeafbba..afd954b 100644
--- a/sc/inc/documentimport.hxx
+++ b/sc/inc/documentimport.hxx
@@ -15,10 +15,13 @@
 
 #include "rtl/ustring.hxx"
 
+#include <boost/noncopyable.hpp>
+
 class ScDocument;
 class ScAddress;
 class ScTokenArray;
 class ScBaseCell;
+struct ScDocumentImportImpl;
 
 /**
  * Accessor class to ScDocument.  Its purpose is to allow import filter to
@@ -27,15 +30,15 @@ class ScBaseCell;
  * position calculation, or anything else that requires expensive
  * computation which are unnecessary and undesirable during import.
  */
-class SC_DLLPUBLIC ScDocumentImport
+class SC_DLLPUBLIC ScDocumentImport : boost::noncopyable
 {
-    ScDocument& mrDoc;
+    ScDocumentImportImpl* mpImpl;
 
     ScDocumentImport(); // disabled
 
 public:
     ScDocumentImport(ScDocument& rDoc);
-    ScDocumentImport(const ScDocumentImport& r);
+    ~ScDocumentImport();
 
     ScDocument& getDoc();
     const ScDocument& getDoc() const;
diff --git a/sc/inc/mtvelements.hxx b/sc/inc/mtvelements.hxx
index b21f7c7..1628381 100644
--- a/sc/inc/mtvelements.hxx
+++ b/sc/inc/mtvelements.hxx
@@ -10,6 +10,7 @@
 #ifndef SC_MTVELEMENTS_HXX
 #define SC_MTVELEMENTS_HXX
 
+#include "address.hxx"
 #include "svl/broadcast.hxx"
 
 #define DEBUG_COLUMN_STORAGE 0
@@ -25,6 +26,10 @@
 #include <mdds/multi_type_vector.hpp>
 #include <mdds/multi_type_vector_custom_func1.hpp>
 
+#include <boost/unordered_map.hpp>
+
+class ScDocument;
+
 namespace sc {
 
 struct CellTextAttr
@@ -74,6 +79,20 @@ struct ColumnBlockPosition
     CellTextAttrStoreType::iterator miCellTextAttrPos;
 };
 
+class ColumnBlockPositionSet
+{
+    typedef boost::unordered_map<SCCOL, ColumnBlockPosition> ColumnsType;
+    typedef boost::unordered_map<SCTAB, ColumnsType> TablesType;
+
+    ScDocument& mrDoc;
+    TablesType maTables;
+
+public:
+    ColumnBlockPositionSet(ScDocument& rDoc);
+
+    ColumnBlockPosition* getBlockPosition(SCTAB nTab, SCCOL nCol);
+};
+
 }
 
 #endif
diff --git a/sc/source/core/data/documentimport.cxx b/sc/source/core/data/documentimport.cxx
index c92688f..ced0936 100644
--- a/sc/source/core/data/documentimport.cxx
+++ b/sc/source/core/data/documentimport.cxx
@@ -15,24 +15,36 @@
 #include "formulacell.hxx"
 #include "docoptio.hxx"
 #include "globalnames.hxx"
+#include "mtvelements.hxx"
 
-ScDocumentImport::ScDocumentImport(ScDocument& rDoc) : mrDoc(rDoc) {}
-ScDocumentImport::ScDocumentImport(const ScDocumentImport& r) : mrDoc(r.mrDoc) {}
+struct ScDocumentImportImpl
+{
+    ScDocument& mrDoc;
+    sc::ColumnBlockPositionSet maBlockPosSet;
+
+    ScDocumentImportImpl(ScDocument& rDoc) : mrDoc(rDoc), maBlockPosSet(rDoc) {}
+};
+
+ScDocumentImport::ScDocumentImport(ScDocument& rDoc) : mpImpl(new ScDocumentImportImpl(rDoc)) {}
+ScDocumentImport::~ScDocumentImport()
+{
+    delete mpImpl;
+}
 
 ScDocument& ScDocumentImport::getDoc()
 {
-    return mrDoc;
+    return mpImpl->mrDoc;
 }
 
 const ScDocument& ScDocumentImport::getDoc() const
 {
-    return mrDoc;
+    return mpImpl->mrDoc;
 }
 
 SCTAB ScDocumentImport::getSheetIndex(const OUString& rName) const
 {
     SCTAB nTab = -1;
-    if (!mrDoc.GetTable(rName, nTab))
+    if (!mpImpl->mrDoc.GetTable(rName, nTab))
         return -1;
 
     return nTab;
@@ -40,33 +52,34 @@ SCTAB ScDocumentImport::getSheetIndex(const OUString& rName) const
 
 SCTAB ScDocumentImport::getSheetCount() const
 {
-    return mrDoc.maTabs.size();
+    return mpImpl->mrDoc.maTabs.size();
 }
 
 bool ScDocumentImport::appendSheet(const OUString& rName)
 {
-    SCTAB nTabCount = mrDoc.maTabs.size();
+    SCTAB nTabCount = mpImpl->mrDoc.maTabs.size();
     if (!ValidTab(nTabCount))
         return false;
 
-    mrDoc.maTabs.push_back(new ScTable(&mrDoc, nTabCount, rName));
+    mpImpl->mrDoc.maTabs.push_back(new ScTable(&mpImpl->mrDoc, nTabCount, rName));
     return true;
 }
 
 void ScDocumentImport::setOriginDate(sal_uInt16 nYear, sal_uInt16 nMonth, sal_uInt16 nDay)
 {
-    if (!mrDoc.pDocOptions)
-        mrDoc.pDocOptions = new ScDocOptions;
+    if (!mpImpl->mrDoc.pDocOptions)
+        mpImpl->mrDoc.pDocOptions = new ScDocOptions;
 
-    mrDoc.pDocOptions->SetDate(nDay, nMonth, nYear);
+    mpImpl->mrDoc.pDocOptions->SetDate(nDay, nMonth, nYear);
 }
 
 void ScDocumentImport::setAutoInput(const ScAddress& rPos, const OUString& rStr)
 {
-    if (!mrDoc.TableExists(rPos.Tab()))
+    if (!mpImpl->mrDoc.TableExists(rPos.Tab()))
         return;
 
-    mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()].SetString(rPos.Row(), rPos.Tab(), rStr, mrDoc.GetAddressConvention());
+    mpImpl->mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()].SetString(
+        rPos.Row(), rPos.Tab(), rStr, mpImpl->mrDoc.GetAddressConvention());
 }
 
 void ScDocumentImport::setNumericCell(const ScAddress& rPos, double fVal)
@@ -82,18 +95,18 @@ void ScDocumentImport::setStringCell(const ScAddress& rPos, const OUString& rStr
 void ScDocumentImport::setFormulaCell(
     const ScAddress& rPos, const OUString& rFormula, formula::FormulaGrammar::Grammar eGrammar)
 {
-    insertCell(rPos, new ScFormulaCell(&mrDoc, rPos, rFormula, eGrammar));
+    insertCell(rPos, new ScFormulaCell(&mpImpl->mrDoc, rPos, rFormula, eGrammar));
 }
 
 void ScDocumentImport::setFormulaCell(const ScAddress& rPos, const ScTokenArray& rArray)
 {
-    insertCell(rPos, new ScFormulaCell(&mrDoc, rPos, &rArray));
+    insertCell(rPos, new ScFormulaCell(&mpImpl->mrDoc, rPos, &rArray));
 }
 
 void ScDocumentImport::finalize()
 {
     // Populate the text width and script type arrays in all columns.
-    ScDocument::TableContainer::iterator itTab = mrDoc.maTabs.begin(), itTabEnd = mrDoc.maTabs.end();
+    ScDocument::TableContainer::iterator itTab = mpImpl->mrDoc.maTabs.begin(), itTabEnd = mpImpl->mrDoc.maTabs.end();
     for (; itTab != itTabEnd; ++itTab)
     {
         if (!*itTab)
@@ -119,14 +132,18 @@ void ScDocumentImport::finalize()
 
 void ScDocumentImport::insertCell(const ScAddress& rPos, ScBaseCell* pCell)
 {
-    if (!mrDoc.TableExists(rPos.Tab()))
+    if (!mpImpl->mrDoc.TableExists(rPos.Tab()))
     {
         pCell->Delete();
         return;
     }
 
-    ScColumn& rCol = mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()];
-    rCol.SetCell(rPos.Row(), pCell);
+    ScColumn& rCol = mpImpl->mrDoc.maTabs[rPos.Tab()]->aCol[rPos.Col()];
+    sc::ColumnBlockPosition* p = mpImpl->maBlockPosSet.getBlockPosition(rPos.Tab(), rPos.Col());
+    if (p)
+        rCol.SetCell(*p, rPos.Row(), pCell);
+    else
+        rCol.SetCell(rPos.Row(), pCell);
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/mtvelements.cxx b/sc/source/core/data/mtvelements.cxx
index 45c06dd..efe07cf 100644
--- a/sc/source/core/data/mtvelements.cxx
+++ b/sc/source/core/data/mtvelements.cxx
@@ -9,6 +9,7 @@
 
 #include "mtvelements.hxx"
 #include "globalnames.hxx"
+#include "document.hxx"
 
 namespace sc {
 
@@ -24,6 +25,45 @@ CellTextAttr::CellTextAttr(sal_uInt16 nTextWidth, sal_uInt8 nScriptType) :
     mnTextWidth(nTextWidth),
     mnScriptType(nScriptType) {}
 
+ColumnBlockPositionSet::ColumnBlockPositionSet(ScDocument& rDoc) : mrDoc(rDoc) {}
+
+ColumnBlockPosition* ColumnBlockPositionSet::getBlockPosition(SCTAB nTab, SCCOL nCol)
+{
+    TablesType::iterator itTab = maTables.find(nTab);
+    if (itTab == maTables.end())
+    {
+        std::pair<TablesType::iterator,bool> r =
+            maTables.insert(TablesType::value_type(nTab, ColumnsType()));
+        if (!r.second)
+            // insertion failed.
+            return NULL;
+
+        itTab = r.first;
+    }
+
+    ColumnsType& rCols = itTab->second;
+
+    ColumnsType::iterator it = rCols.find(nCol);
+    if (it != rCols.end())
+        // Block position for this column has already been fetched.
+        return &it->second;
+
+    std::pair<ColumnsType::iterator,bool> r =
+        rCols.insert(
+            ColumnsType::value_type(nCol, ColumnBlockPosition()));
+
+    if (!r.second)
+        // insertion failed.
+        return NULL;
+
+    it = r.first;
+
+    if (!mrDoc.InitColumnBlockPosition(it->second, nTab, nCol))
+        return NULL;
+
+    return &it->second;
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 70dfd2dff34dc85b72221c5b74b73b4f9cfc0957
Author: Kohei Yoshida <kohei.yoshida at gmail.com>
Date:   Fri May 17 15:08:23 2013 -0400

    Same with MixDocument(). But this one has additional bottleneck...
    
    With the removal of existing cells. So, this change is not enough to
    make this operation fly.
    
    Change-Id: Ic468375f6d0c28e2cc7d5391fb0565d53ee7fb4e

diff --git a/sc/inc/clipcontext.hxx b/sc/inc/clipcontext.hxx
index 4d9224f..599da6e 100644
--- a/sc/inc/clipcontext.hxx
+++ b/sc/inc/clipcontext.hxx
@@ -90,6 +90,13 @@ public:
     virtual ~CopyToDocContext();
 };
 
+class MixDocContext : public ClipContextBase
+{
+public:
+    MixDocContext(ScDocument& rDoc);
+    virtual ~MixDocContext();
+};
+
 }
 
 #endif
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 0e7ed53..c71ffea 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -41,6 +41,7 @@ namespace sc {
     class CopyFromClipContext;
     class CopyToClipContext;
     class CopyToDocContext;
+    class MixDocContext;
     struct ColumnBlockPosition;
 }
 
@@ -231,10 +232,12 @@ public:
     void        RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow );
 
                 //  Selection (?) of this document
-    void        MixMarked( const ScMarkData& rMark, sal_uInt16 nFunction,
-                            bool bSkipEmpty, ScColumn& rSrcCol );
-    void        MixData( SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, bool bSkipEmpty,
-                            ScColumn& rSrcCol );
+    void MixMarked(
+        sc::MixDocContext& rCxt, const ScMarkData& rMark, sal_uInt16 nFunction,
+        bool bSkipEmpty, const ScColumn& rSrcCol );
+    void MixData(
+        sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction, bool bSkipEmpty,
+        const ScColumn& rSrcCol );
 
     ScFormulaCell*  CreateRefCell( ScDocument* pDestDoc, const ScAddress& rDestPos,
                                     SCSIZE nIndex, sal_uInt16 nFlags ) const;
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 5f63d85..f01e811 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1988,6 +1988,8 @@ private: // CLOOK-Impl-methods
     };
 
     bool TableExists( SCTAB nTab ) const;
+    ScTable* FetchTable( SCTAB nTab );
+    const ScTable* FetchTable( SCTAB nTab ) const;
 
     void    MergeNumberFormatter(ScDocument* pSrcDoc);
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 7d0836f..f04a9bb 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -53,6 +53,7 @@ namespace sc {
     class CopyFromClipContext;
     class CopyToClipContext;
     class CopyToDocContext;
+    class MixDocContext;
     struct ColumnBlockPosition;
 }
 
@@ -412,10 +413,13 @@ public:
                                 ScTable* pTransClip, sal_uInt16 nFlags, bool bAsLink );
 
                 // mark of this document
-    void        MixMarked( const ScMarkData& rMark, sal_uInt16 nFunction,
-                            bool bSkipEmpty, ScTable* pSrcTab );
-    void        MixData( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
-                            sal_uInt16 nFunction, bool bSkipEmpty, ScTable* pSrcTab );
+    void MixMarked(
+        sc::MixDocContext& rCxt, const ScMarkData& rMark, sal_uInt16 nFunction,
+        bool bSkipEmpty, const ScTable* pSrcTab );
+
+    void MixData(
+        sc::MixDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+        sal_uInt16 nFunction, bool bSkipEmpty, const ScTable* pSrcTab );
 
     void        CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
                             SCCOL nDestCol, SCROW nDestRow, SCTAB nDestTab );
diff --git a/sc/source/core/data/clipcontext.cxx b/sc/source/core/data/clipcontext.cxx
index 8777aa3..2c7abdf 100644
--- a/sc/source/core/data/clipcontext.cxx
+++ b/sc/source/core/data/clipcontext.cxx
@@ -120,6 +120,9 @@ bool CopyToClipContext::isCloneNotes() const
 CopyToDocContext::CopyToDocContext(ScDocument& rDoc) : ClipContextBase(rDoc) {}
 CopyToDocContext::~CopyToDocContext() {}
 
+MixDocContext::MixDocContext(ScDocument& rDoc) : ClipContextBase(rDoc) {}
+MixDocContext::~MixDocContext() {}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index a29f791..b530184 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -828,8 +828,9 @@ ScBaseCell* ScColumn::CloneCell(
 }
 
 
-void ScColumn::MixMarked( const ScMarkData& rMark, sal_uInt16 nFunction,
-                            bool bSkipEmpty, ScColumn& rSrcCol )
+void ScColumn::MixMarked(
+    sc::MixDocContext& rCxt, const ScMarkData& rMark, sal_uInt16 nFunction,
+    bool bSkipEmpty, const ScColumn& rSrcCol )
 {
     SCROW nRow1, nRow2;
 
@@ -837,7 +838,7 @@ void ScColumn::MixMarked( const ScMarkData& rMark, sal_uInt16 nFunction,
     {
         ScMarkArrayIter aIter( rMark.GetArray()+nCol );
         while (aIter.Next( nRow1, nRow2 ))
-            MixData( nRow1, nRow2, nFunction, bSkipEmpty, rSrcCol );
+            MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, rSrcCol);
     }
 }
 
@@ -885,9 +886,9 @@ static void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell )
 }
 
 
-void ScColumn::MixData( SCROW nRow1, SCROW nRow2,
-                            sal_uInt16 nFunction, bool bSkipEmpty,
-                            ScColumn& rSrcCol )
+void ScColumn::MixData(
+    sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, sal_uInt16 nFunction,
+    bool bSkipEmpty, const ScColumn& rSrcCol )
 {
     SCSIZE nSrcCount = rSrcCol.maItems.size();
 
@@ -1023,12 +1024,18 @@ void ScColumn::MixData( SCROW nRow1, SCROW nRow2,
 
         if ( pNew || bDelete ) // New result?
         {
+            sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
             if (pDest && !pNew) // Old cell present?
             {
                 Delete(nRow); // -> Delete
             }
             if (pNew)
-                Insert(nRow, pNew); // Insert new one
+            {
+                if (p)
+                    Insert(*p, nRow, pNew);
+                else
+                    Insert(nRow, pNew); // Insert new one
+            }
 
             Search( nRow, nIndex ); // Everything could have moved
             if (pNew)
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index cf2ae95..decaae9 100644

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list