[Libreoffice-commits] core.git: 7 commits - sc/inc sc/source

Eike Rathke erack at redhat.com
Fri Sep 4 12:15:26 PDT 2015


 sc/inc/dbdata.hxx                |   43 +++++
 sc/inc/document.hxx              |    1 
 sc/source/core/data/documen3.cxx |    6 
 sc/source/core/tool/dbdata.cxx   |  282 ++++++++++++++++++++++++++++++---------
 sc/source/ui/docshell/docsh.cxx  |    1 
 5 files changed, 264 insertions(+), 69 deletions(-)

New commits:
commit f8c14e81cb63ea077109a4b0147e716cae41ab1b
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 20:44:10 2015 +0200

    TablreRef: always use SetTableColumnName() to prevent duplicates
    
    Change-Id: Ifbdd9b0c3d8e6f41c4d1eb4d0e62053a8788e05d

diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 852c511..d9bfcd2 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -738,12 +738,21 @@ private:
 };
 
 /** Set a numbered table column name at given nIndex, preventing duplicates,
-    numbering starting at nCount. No check whether nIndex is valid. */
-void SetTableColumnName( ::std::vector<OUString>& rVec, size_t nIndex, const OUString& rName, sal_Int32 nCount )
+    numbering starting at nCount. If nCount==0 then the first attempt is made
+    with an unnumbered name and if already present the next attempt with
+    nCount=2, so "Original" and "Original2". No check whether nIndex is valid. */
+void SetTableColumnName( ::std::vector<OUString>& rVec, size_t nIndex, const OUString& rName, size_t nCount )
 {
+    OUString aStr;
     do
     {
-        OUString aStr( rName + OUString::number( nCount));
+        if (nCount)
+            aStr = rName + OUString::number( nCount);
+        else
+        {
+            aStr = rName;
+            ++nCount;
+        }
         auto it( ::std::find_if( rVec.begin(), rVec.end(), TableColumnNameSearch( aStr)));
         if (it == rVec.end())
         {
@@ -773,11 +782,14 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
             if (pCell->hasString())
             {
                 const OUString& rStr = pCell->getString( pDoc);
-                aNewNames[nCol-nStartCol] = rStr;
                 if (rStr.isEmpty())
                     bHaveEmpty = true;
-                else if (nLastColFilled < nCol-1)
-                    bHaveEmpty = true;
+                else
+                {
+                    SetTableColumnName( aNewNames, nCol-nStartCol, rStr, 0);
+                    if (nLastColFilled < nCol-1)
+                        bHaveEmpty = true;
+                }
                 nLastColFilled = nCol;
             }
             else
@@ -799,13 +811,7 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
                 if (rStr.isEmpty())
                     bHaveEmpty = true;
                 else
-                {
-                    auto it( ::std::find_if( aNewNames.begin(), aNewNames.end(), TableColumnNameSearch( rStr)));
-                    if (it == aNewNames.end())
-                        aNewNames[i] = rStr;
-                    else
-                        bHaveEmpty = true;
-                }
+                    SetTableColumnName( aNewNames, i, rStr, 0);
             }
         }
     }
commit bc77f2c67c747b89e2c661ed86b8b48acd8ce317
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 19:58:22 2015 +0200

    use GetHeaderArea() for simplification
    
    Change-Id: Ib06b35a117db3c02826cb18754f7ca11875c91c8

diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index 7584f67..36e0aa0 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -143,6 +143,8 @@ public:
     void        SetStripData(bool bSet)         { bStripData = bSet; }
 
     void        SetContainer( ScDBDataContainerBase* pContainer ) { mpContainer = pContainer; }
+    /** Returns header row range if has headers, else invalid range. */
+    ScRange     GetHeaderArea() const;
     void        StartTableColumnNamesListener();
     void        EndTableColumnNamesListener();
     SC_DLLPUBLIC void SetTableColumnNames( const ::std::vector< OUString >& rNames );
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index f7588f8..852c511 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -192,12 +192,7 @@ ScDBData& ScDBData::operator= (const ScDBData& rData)
         {
             mbTableColumnNamesDirty = true;
             if (mpContainer)
-            {
-                ScRange aHeaderRange( ScAddress::UNINITIALIZED);
-                GetArea( aHeaderRange);
-                aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
-                mpContainer->GetDirtyTableColumnNames().Join( aHeaderRange);
-            }
+                mpContainer->GetDirtyTableColumnNames().Join( GetHeaderArea());
         }
         else
         {
@@ -323,6 +318,13 @@ void ScDBData::GetArea(ScRange& rRange) const
     rRange = ScRange( nStartCol, nStartRow, nTable, nEndCol, nNewEndRow, nTable );
 }
 
+ScRange ScDBData::GetHeaderArea() const
+{
+    if (HasHeader())
+        return ScRange( nStartCol, nStartRow, nTable, nEndCol, nStartRow, nTable);
+    return ScRange( ScAddress::INITIALIZE_INVALID);
+}
+
 void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
 {
     bool bHeaderRangeChange = (nTab != nTable || nCol1 != nStartCol || nCol2 != nEndCol || nRow1 != nStartRow);
@@ -654,12 +656,7 @@ void ScDBData::StartTableColumnNamesListener()
     {
         ScDocument* pDoc = mpContainer->GetDocument();
         if (!pDoc->IsClipOrUndo())
-        {
-            ScRange aHeaderRange( ScAddress::UNINITIALIZED);
-            GetArea( aHeaderRange);
-            aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
-            pDoc->StartListeningArea( aHeaderRange, false, this);
-        }
+            pDoc->StartListeningArea( GetHeaderArea(), false, this);
     }
 }
 
@@ -839,10 +836,7 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange
     }
 
     // Check if this is affected for the range requested.
-    ScRange aHeaderRange( ScAddress::UNINITIALIZED);
-    GetArea( aHeaderRange);
-    aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
-    ScRange aIntersection( aHeaderRange.Intersection( rRange));
+    ScRange aIntersection( GetHeaderArea().Intersection( rRange));
     if (!aIntersection.IsValid())
         return;
 
commit 04d3e135ec9ff587672c8c7b0f4b8ea0e9601b79
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 20:24:37 2015 +0200

    setup header area listening only if not clipboard/undo
    
    ScDBData checks that also internally, but added dirty ranges would
    linger around until container will be destructed, plus spare some
    cycles.
    
    Change-Id: I6c20b227117f272169129978bd8b26ccea9e1a72

diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 252f5c5..f7588f8 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -1041,13 +1041,16 @@ ScDBCollection::NamedDBs::NamedDBs(const NamedDBs& r)
         if (m_DBs.insert( std::move(pData)).second)
         {
             p->SetContainer( this);
-            p->StartTableColumnNamesListener(); // needs the container be set already
-            if (p->AreTableColumnNamesDirty())
+            if (!mrDoc.IsClipOrUndo())
             {
-                // Refresh table column names in next round.
-                ScRange aHeader( p->GetHeaderArea());
-                if (aHeader.IsValid())
-                    maDirtyTableColumnNames.Join( aHeader);
+                p->StartTableColumnNamesListener(); // needs the container be set already
+                if (p->AreTableColumnNamesDirty())
+                {
+                    // Refresh table column names in next round.
+                    ScRange aHeader( p->GetHeaderArea());
+                    if (aHeader.IsValid())
+                        maDirtyTableColumnNames.Join( aHeader);
+                }
             }
         }
     }
@@ -1108,13 +1111,16 @@ bool ScDBCollection::NamedDBs::insert(ScDBData* p)
     if (r.second)
     {
         p->SetContainer( this);
-        p->StartTableColumnNamesListener(); // needs the container be set already
-        if (p->AreTableColumnNamesDirty())
+        if (!mrDoc.IsClipOrUndo())
         {
-            // Refresh table column names in next round.
-            ScRange aHeader( p->GetHeaderArea());
-            if (aHeader.IsValid())
-                maDirtyTableColumnNames.Join( aHeader);
+            p->StartTableColumnNamesListener(); // needs the container be set already
+            if (p->AreTableColumnNamesDirty())
+            {
+                // Refresh table column names in next round.
+                ScRange aHeader( p->GetHeaderArea());
+                if (aHeader.IsValid())
+                    maDirtyTableColumnNames.Join( aHeader);
+            }
         }
 
         /* TODO: shouldn't the import refresh not be setup for
commit 0d5645f4fa069677b404899f126bd57e3035fd9b
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 20:07:16 2015 +0200

    TableRef: add an initial dirty range for newly inserted ScDBData
    
    ... so table column names get updated in the next run.
    
    Change-Id: I998d610a3df14fb80b6222fd1a5b61847810a722

diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 1b98b00..252f5c5 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -1042,6 +1042,13 @@ ScDBCollection::NamedDBs::NamedDBs(const NamedDBs& r)
         {
             p->SetContainer( this);
             p->StartTableColumnNamesListener(); // needs the container be set already
+            if (p->AreTableColumnNamesDirty())
+            {
+                // Refresh table column names in next round.
+                ScRange aHeader( p->GetHeaderArea());
+                if (aHeader.IsValid())
+                    maDirtyTableColumnNames.Join( aHeader);
+            }
         }
     }
 }
@@ -1102,6 +1109,13 @@ bool ScDBCollection::NamedDBs::insert(ScDBData* p)
     {
         p->SetContainer( this);
         p->StartTableColumnNamesListener(); // needs the container be set already
+        if (p->AreTableColumnNamesDirty())
+        {
+            // Refresh table column names in next round.
+            ScRange aHeader( p->GetHeaderArea());
+            if (aHeader.IsValid())
+                maDirtyTableColumnNames.Join( aHeader);
+        }
 
         /* TODO: shouldn't the import refresh not be setup for
          * clipboard/undo documents? It was already like this before.. */
commit 757996aa2879a2ea850055bafa60edb66548dffd
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 19:44:47 2015 +0200

    TableRef: unconditionally init dirty and check for refresh
    
    Change-Id: I7746cca2d32d242b6300311fbe7bfe63caf9dfc9

diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 6d0c62b..1b98b00 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -77,7 +77,7 @@ ScDBData::ScDBData( const OUString& rName,
     nIndex      (0),
     bAutoFilter (false),
     bModified   (false),
-    mbTableColumnNamesDirty(bHasH)
+    mbTableColumnNamesDirty(true)
 {
     aUpper = ScGlobal::pCharClass->uppercase(aUpper);
 }
@@ -832,12 +832,13 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
 void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange )
 {
     // Header-less tables get names generated, completely empty a full refresh.
-    if (!HasHeader() || maTableColumnNames.empty())
+    if (mbTableColumnNamesDirty && (!HasHeader() || maTableColumnNames.empty()))
     {
         RefreshTableColumnNames( pDoc);
         return;
     }
 
+    // Check if this is affected for the range requested.
     ScRange aHeaderRange( ScAddress::UNINITIALIZED);
     GetArea( aHeaderRange);
     aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
commit 31ab5d7140400c5f1c2493656e032599ea601555
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 18:45:08 2015 +0200

    TableRef: fully refresh table column names also for partial range
    
    Only one cell of a range was broadcasted per area listener if multiple
    cells were affected. We don't know if there were more.
    
    We could set up cell listeners instead, but that's not worth the hassle,
    header cells don't change that often and if changed the header range to
    refresh will not be overly large.
    
    Change-Id: I915101b809fc1824803f10e696c52fb2185117f7

diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 42a2c81..6d0c62b 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -845,43 +845,10 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange
     if (!aIntersection.IsValid())
         return;
 
-    // Full refresh if sizes don't match.
-    if (maTableColumnNames.size() < static_cast<size_t>(aIntersection.aEnd.Col() - nStartCol + 1))
-    {
-        RefreshTableColumnNames( pDoc);
-        return;
-    }
-
-    // Update column names from cells in intersecting header range, but don't
-    // set names to empty string.
-    ScHorizontalCellIterator aIter( pDoc, nTable,
-            aIntersection.aStart.Col(), nStartRow, aIntersection.aEnd.Col(), nStartRow);
-    ScRefCellValue* pCell;
-    SCCOL nCol;
-    SCROW nRow;
-    while((pCell = aIter.GetNext( nCol, nRow)) != nullptr)
-    {
-        size_t nOff = nCol - nStartCol;
-        bool bEmpty = maTableColumnNames[nOff].isEmpty();
-        if (!pCell->hasString())
-            bEmpty &= true;
-        else
-        {
-            const OUString& rStr = pCell->getString( pDoc);
-            if (rStr.isEmpty())
-                bEmpty &= true;
-            else
-                maTableColumnNames[nOff] = rStr;
-        }
-        if (bEmpty)
-        {
-            OUString aColumn( ScGlobal::GetRscString(STR_COLUMN));
-            SetTableColumnName( maTableColumnNames, nOff, aColumn, nOff+1);
-        }
-    }
-
-    if (aIntersection == aHeaderRange)
-        mbTableColumnNamesDirty = false;
+    // Always fully refresh, only one cell of a range was broadcasted per area
+    // listener if multiple cells were affected. We don't know if there were
+    // more.
+    RefreshTableColumnNames( pDoc);
 }
 
 sal_Int32 ScDBData::GetColumnNameOffset( const OUString& rName ) const
commit 679a5dc0cf838579e320dd199f02a9a8300c8820
Author: Eike Rathke <erack at redhat.com>
Date:   Fri Sep 4 18:30:22 2015 +0200

    TableRef: update table column names when cell content changed
    
    Change-Id: Id699358c7dae635b13ed4b981326a6490255a4d4

diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx
index bf1ac4c..7584f67 100644
--- a/sc/inc/dbdata.hxx
+++ b/sc/inc/dbdata.hxx
@@ -24,6 +24,9 @@
 #include "refreshtimer.hxx"
 #include "address.hxx"
 #include "global.hxx"
+#include "rangelst.hxx"
+
+#include <svl/listener.hxx>
 
 #include <boost/scoped_ptr.hpp>
 
@@ -45,7 +48,21 @@ enum class ScDBDataPortion
     AREA        ///< entire area
 };
 
-class ScDBData : public ScRefreshTimer
+/** Container base class to provide selected access for ScDBData. */
+class ScDBDataContainerBase
+{
+public:
+    ScDBDataContainerBase( ScDocument& rDoc ) : mrDoc(rDoc) {}
+    virtual ~ScDBDataContainerBase() {}
+    ScDocument* GetDocument() const;
+    ScRangeList& GetDirtyTableColumnNames();
+
+protected:
+    ScDocument& mrDoc;
+    ScRangeList maDirtyTableColumnNames;
+};
+
+class ScDBData : public SvtListener, public ScRefreshTimer
 {
 private:
     boost::scoped_ptr<ScSortParam> mpSortParam;
@@ -53,6 +70,8 @@ private:
     boost::scoped_ptr<ScSubTotalParam> mpSubTotal;
     boost::scoped_ptr<ScImportParam> mpImportParam;
 
+    ScDBDataContainerBase* mpContainer;
+
     /// DBParam
     const OUString aName;
     OUString aUpper;
@@ -79,6 +98,7 @@ private:
     bool            bModified;          ///< is set/cleared for/by(?) UpdateReference
 
     ::std::vector< OUString > maTableColumnNames;   ///< names of table columns
+    bool            mbTableColumnNamesDirty;
 
     using ScRefreshTimer::operator==;
 
@@ -96,7 +116,9 @@ public:
     ScDBData(const OUString& rName, const ScDBData& rData);
     virtual ~ScDBData();
 
-    ScDBData&   operator= (const ScDBData& rData);
+    virtual void Notify( const SfxHint& rHint ) SAL_OVERRIDE;
+
+    ScDBData&   operator= (const ScDBData& rData) ;
 
     bool        operator== (const ScDBData& rData) const;
 
@@ -119,8 +141,13 @@ public:
     void        SetKeepFmt(bool bSet)           { bKeepFmt = bSet; }
     bool        IsStripData() const             { return bStripData; }
     void        SetStripData(bool bSet)         { bStripData = bSet; }
-    void        SetTableColumnNames( const ::std::vector< OUString >& rNames ) { maTableColumnNames = rNames; }
-    const ::std::vector< OUString >&    GetTableColumnNames() const { return maTableColumnNames; }
+
+    void        SetContainer( ScDBDataContainerBase* pContainer ) { mpContainer = pContainer; }
+    void        StartTableColumnNamesListener();
+    void        EndTableColumnNamesListener();
+    SC_DLLPUBLIC void SetTableColumnNames( const ::std::vector< OUString >& rNames );
+    SC_DLLPUBLIC const ::std::vector< OUString >& GetTableColumnNames() const { return maTableColumnNames; }
+    bool        AreTableColumnNamesDirty() const { return mbTableColumnNamesDirty; }
 
     /** Refresh/update the column names with the header row's cell contents. */
     SC_DLLPUBLIC void RefreshTableColumnNames( ScDocument* pDoc );
@@ -202,16 +229,16 @@ public:
     /**
      * Stores global named database ranges.
      */
-    class SC_DLLPUBLIC NamedDBs
+    class SC_DLLPUBLIC NamedDBs : public ScDBDataContainerBase
     {
         friend class ScDBCollection;
 
         typedef ::std::set<std::unique_ptr<ScDBData>, ScDBData::less> DBsType;
         DBsType m_DBs;
         ScDBCollection& mrParent;
-        ScDocument& mrDoc;
         NamedDBs(ScDBCollection& rParent, ScDocument& rDoc);
         NamedDBs(const NamedDBs& r);
+        virtual ~NamedDBs();
         NamedDBs & operator=(NamedDBs const&) = delete;
 
     public:
@@ -287,6 +314,8 @@ public:
     ScDBData* GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
     ScDBData* GetDBNearCursor(SCCOL nCol, SCROW nRow, SCTAB nTab );
 
+    void RefreshDirtyTableColumnNames();
+
     void    DeleteOnTab( SCTAB nTab );
     void    UpdateReference(UpdateRefMode eUpdateRefMode,
                                 SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index 9ea6729..7016d7f8 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -570,6 +570,7 @@ public:
     ScDBData* GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion);
     const ScDBData* GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const;
     ScDBData* GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2);
+    void RefreshDirtyTableColumnNames();
 
     SC_DLLPUBLIC const ScRangeData* GetRangeAtBlock( const ScRange& rBlock, OUString* pName=NULL ) const;
 
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
index 32791da..0e6998a 100644
--- a/sc/source/core/data/documen3.cxx
+++ b/sc/source/core/data/documen3.cxx
@@ -313,6 +313,12 @@ ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nC
         return NULL;
 }
 
+void ScDocument::RefreshDirtyTableColumnNames()
+{
+    if (pDBCollection)
+        pDBCollection->RefreshDirtyTableColumnNames();
+}
+
 bool ScDocument::HasPivotTable() const
 {
     return pDPCollection && pDPCollection->GetCount();
diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx
index 8375f55..42a2c81 100644
--- a/sc/source/core/tool/dbdata.cxx
+++ b/sc/source/core/tool/dbdata.cxx
@@ -32,6 +32,7 @@
 #include "subtotalparam.hxx"
 #include "sortparam.hxx"
 #include "dociter.hxx"
+#include "brdcst.hxx"
 
 #include <memory>
 #include <utility>
@@ -52,10 +53,12 @@ ScDBData::ScDBData( const OUString& rName,
                     SCTAB nTab,
                     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
                     bool bByR, bool bHasH, bool bTotals) :
+    // Listeners are to be setup by the "parent" container.
     mpSortParam(new ScSortParam),
     mpQueryParam(new ScQueryParam),
     mpSubTotal(new ScSubTotalParam),
     mpImportParam(new ScImportParam),
+    mpContainer (nullptr),
     aName       (rName),
     aUpper      (rName),
     nTable      (nTab),
@@ -73,17 +76,21 @@ ScDBData::ScDBData( const OUString& rName,
     bDBSelection(false),
     nIndex      (0),
     bAutoFilter (false),
-    bModified   (false)
+    bModified   (false),
+    mbTableColumnNamesDirty(bHasH)
 {
     aUpper = ScGlobal::pCharClass->uppercase(aUpper);
 }
 
 ScDBData::ScDBData( const ScDBData& rData ) :
+    // Listeners are to be setup by the "parent" container.
+    SvtListener         (),
     ScRefreshTimer      ( rData ),
     mpSortParam(new ScSortParam(*rData.mpSortParam)),
     mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
     mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
     mpImportParam(new ScImportParam(*rData.mpImportParam)),
+    mpContainer         (nullptr),
     aName               (rData.aName),
     aUpper              (rData.aUpper),
     nTable              (rData.nTable),
@@ -103,16 +110,20 @@ ScDBData::ScDBData( const ScDBData& rData ) :
     nIndex              (rData.nIndex),
     bAutoFilter         (rData.bAutoFilter),
     bModified           (rData.bModified),
-    maTableColumnNames  (rData.maTableColumnNames)
+    maTableColumnNames  (rData.maTableColumnNames),
+    mbTableColumnNamesDirty(rData.mbTableColumnNamesDirty)
 {
 }
 
 ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) :
+    // Listeners are to be setup by the "parent" container.
+    SvtListener         (),
     ScRefreshTimer      ( rData ),
     mpSortParam(new ScSortParam(*rData.mpSortParam)),
     mpQueryParam(new ScQueryParam(*rData.mpQueryParam)),
     mpSubTotal(new ScSubTotalParam(*rData.mpSubTotal)),
     mpImportParam(new ScImportParam(*rData.mpImportParam)),
+    mpContainer         (nullptr),
     aName               (rName),
     aUpper              (rName),
     nTable              (rData.nTable),
@@ -132,7 +143,8 @@ ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) :
     nIndex              (rData.nIndex),
     bAutoFilter         (rData.bAutoFilter),
     bModified           (rData.bModified),
-    maTableColumnNames  (rData.maTableColumnNames)
+    maTableColumnNames  (rData.maTableColumnNames),
+    mbTableColumnNamesDirty (rData.mbTableColumnNamesDirty)
 {
     aUpper = ScGlobal::pCharClass->uppercase(aUpper);
 }
@@ -141,11 +153,20 @@ ScDBData& ScDBData::operator= (const ScDBData& rData)
 {
     // Don't modify the name.  The name is not mutable as it is used as a key
     // in the container to keep the db ranges sorted by the name.
+
+    bool bHeaderRangeDiffers = (nTable != rData.nTable || nStartCol != rData.nStartCol ||
+            nEndCol != rData.nEndCol || nStartRow != rData.nStartRow);
+    bool bNeedsListening = ((bHasHeader && bHeaderRangeDiffers) || (!bHasHeader && rData.bHasHeader));
+    if (bHasHeader && (!rData.bHasHeader || bHeaderRangeDiffers))
+    {
+        EndTableColumnNamesListener();
+    }
     ScRefreshTimer::operator=( rData );
     mpSortParam.reset(new ScSortParam(*rData.mpSortParam));
     mpQueryParam.reset(new ScQueryParam(*rData.mpQueryParam));
     mpSubTotal.reset(new ScSubTotalParam(*rData.mpSubTotal));
     mpImportParam.reset(new ScImportParam(*rData.mpImportParam));
+    // Keep mpContainer.
     nTable              = rData.nTable;
     nStartCol           = rData.nStartCol;
     nStartRow           = rData.nStartRow;
@@ -162,7 +183,35 @@ ScDBData& ScDBData::operator= (const ScDBData& rData)
     bDBSelection        = rData.bDBSelection;
     nIndex              = rData.nIndex;
     bAutoFilter         = rData.bAutoFilter;
-    maTableColumnNames  = rData.maTableColumnNames;
+
+    if (bHeaderRangeDiffers)
+    {
+        if (!maTableColumnNames.empty())
+            ::std::vector<OUString>().swap( maTableColumnNames);
+        if (bHasHeader)
+        {
+            mbTableColumnNamesDirty = true;
+            if (mpContainer)
+            {
+                ScRange aHeaderRange( ScAddress::UNINITIALIZED);
+                GetArea( aHeaderRange);
+                aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
+                mpContainer->GetDirtyTableColumnNames().Join( aHeaderRange);
+            }
+        }
+        else
+        {
+            mbTableColumnNamesDirty = false;
+        }
+    }
+    else
+    {
+        maTableColumnNames  = rData.maTableColumnNames;
+        mbTableColumnNamesDirty = rData.mbTableColumnNamesDirty;
+    }
+
+    if (bNeedsListening)
+        StartTableColumnNamesListener();
 
     return *this;
 }
@@ -276,12 +325,15 @@ void ScDBData::GetArea(ScRange& rRange) const
 
 void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
 {
-    if (nCol2 - nCol1 != nEndCol - nStartCol)
+    bool bHeaderRangeChange = (nTab != nTable || nCol1 != nStartCol || nCol2 != nEndCol || nRow1 != nStartRow);
+    if (bHeaderRangeChange)
     {
+        EndTableColumnNamesListener();
         if (!maTableColumnNames.empty())
         {
             SAL_WARN("sc.core", "ScDBData::SetArea - invalidating column names/offsets");
             ::std::vector<OUString>().swap( maTableColumnNames);
+            mbTableColumnNamesDirty = true;
         }
     }
 
@@ -290,6 +342,9 @@ void ScDBData::SetArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW
     nStartRow = nRow1;
     nEndCol   = nCol2;
     nEndRow   = nRow2;
+
+    if (bHeaderRangeChange)
+        StartTableColumnNamesListener();
 }
 
 void ScDBData::MoveTo(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
@@ -507,9 +562,16 @@ void ScDBData::UpdateMoveTab(SCTAB nOldPos, SCTAB nNewPos)
         bool bChanged = ( nTab != aRange.aStart.Tab() );
         if (bChanged)
         {
-            // Same column range, SetArea() does not invalidate column names.
+            // SetArea() invalidates column names, but it is the same column range
+            // just on a different sheet; remember and set new.
+            ::std::vector<OUString> aNames( maTableColumnNames);
+            bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
+            // Same column range.
             SetArea( nTab, aRange.aStart.Col(), aRange.aStart.Row(),
                     aRange.aEnd.Col(),aRange.aEnd.Row() );
+            // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
+            maTableColumnNames = aNames;
+            mbTableColumnNamesDirty = bTableColumnNamesDirty;
         }
 
         //  MoveTo() is not necessary if only the sheet changed.
@@ -538,12 +600,14 @@ void ScDBData::UpdateReference(ScDocument* pDoc, UpdateRefMode eUpdateRefMode,
                                             theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) != UR_NOTHING;
     if (bDoUpdate)
     {
-        // MoveTo() invalidates column names via SetArea(); adjust, remember
-        // and set new column offsets for names.
+        // MoveTo() invalidates column names via SetArea(); adjust, remember and set new.
         AdjustTableColumnNames( eUpdateRefMode, nDx, nCol1, nOldCol1, nOldCol2, theCol1, theCol2);
         ::std::vector<OUString> aNames( maTableColumnNames);
+        bool bTableColumnNamesDirty = mbTableColumnNamesDirty;
         MoveTo( theTab1, theCol1, theRow1, theCol2, theRow2 );
+        // Do not use SetTableColumnNames() because that resets mbTableColumnNamesDirty.
         maTableColumnNames = aNames;
+        mbTableColumnNamesDirty = bTableColumnNamesDirty;
     }
 
     ScRange aRangeAdvSource;
@@ -579,10 +643,37 @@ void ScDBData::ExtendDataArea(ScDocument* pDoc)
         {
             SAL_WARN("sc.core", "ScDBData::ExtendDataArea - invalidating column names/offsets");
             ::std::vector<OUString>().swap( maTableColumnNames);
+            mbTableColumnNamesDirty = true;
         }
     }
 }
 
+void ScDBData::StartTableColumnNamesListener()
+{
+    if (mpContainer && bHasHeader)
+    {
+        ScDocument* pDoc = mpContainer->GetDocument();
+        if (!pDoc->IsClipOrUndo())
+        {
+            ScRange aHeaderRange( ScAddress::UNINITIALIZED);
+            GetArea( aHeaderRange);
+            aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
+            pDoc->StartListeningArea( aHeaderRange, false, this);
+        }
+    }
+}
+
+void ScDBData::EndTableColumnNamesListener()
+{
+    EndListeningAll();
+}
+
+void ScDBData::SetTableColumnNames( const ::std::vector< OUString >& rNames )
+{
+    maTableColumnNames = rNames;
+    mbTableColumnNamesDirty = false;
+}
+
 void ScDBData::AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx, SCCOL nCol1,
         SCCOL nOldCol1, SCCOL nOldCol2, SCCOL nNewCol1, SCCOL nNewCol2 )
 {
@@ -597,6 +688,9 @@ void ScDBData::AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx,
     ::std::vector<OUString> aNewNames;
     if (eUpdateRefMode == URM_INSDEL)
     {
+        if (nDx > 0)
+            mbTableColumnNamesDirty = true;     // inserted columns will have empty names
+
         // nCol1 is the first column of the block that gets shifted, determine
         // the head and tail elements that are to be copied for deletion or
         // insertion.
@@ -624,6 +718,8 @@ void ScDBData::AdjustTableColumnNames( UpdateRefMode eUpdateRefMode, SCCOL nDx,
     SAL_WARN_IF( !maTableColumnNames.empty() && aNewNames.empty(),
             "sc.core", "ScDBData::AdjustTableColumnNames - invalidating column names/offsets");
     aNewNames.swap( maTableColumnNames);
+    if (maTableColumnNames.empty())
+        mbTableColumnNamesDirty = true;
 }
 
 namespace {
@@ -730,22 +826,27 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc )
     }
 
     aNewNames.swap( maTableColumnNames);
+    mbTableColumnNamesDirty = false;
 }
 
 void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange )
 {
-    if (!HasHeader())
+    // Header-less tables get names generated, completely empty a full refresh.
+    if (!HasHeader() || maTableColumnNames.empty())
+    {
+        RefreshTableColumnNames( pDoc);
         return;
+    }
 
-    ScRange aRange( ScAddress::UNINITIALIZED);
-    GetArea( aRange);
-    aRange.aEnd.SetRow( aRange.aStart.Row());
-    ScRange aIntersection( aRange.Intersection( rRange));
+    ScRange aHeaderRange( ScAddress::UNINITIALIZED);
+    GetArea( aHeaderRange);
+    aHeaderRange.aEnd.SetRow( aHeaderRange.aStart.Row());
+    ScRange aIntersection( aHeaderRange.Intersection( rRange));
     if (!aIntersection.IsValid())
         return;
 
-    if (maTableColumnNames.empty() ||
-            maTableColumnNames.size() < static_cast<size_t>(aIntersection.aEnd.Col() - nStartCol + 1))
+    // Full refresh if sizes don't match.
+    if (maTableColumnNames.size() < static_cast<size_t>(aIntersection.aEnd.Col() - nStartCol + 1))
     {
         RefreshTableColumnNames( pDoc);
         return;
@@ -760,20 +861,27 @@ void ScDBData::RefreshTableColumnNames( ScDocument* pDoc, const ScRange& rRange
     SCROW nRow;
     while((pCell = aIter.GetNext( nCol, nRow)) != nullptr)
     {
-        if (pCell->hasString())
+        size_t nOff = nCol - nStartCol;
+        bool bEmpty = maTableColumnNames[nOff].isEmpty();
+        if (!pCell->hasString())
+            bEmpty &= true;
+        else
         {
             const OUString& rStr = pCell->getString( pDoc);
-            if (!rStr.isEmpty())
-                maTableColumnNames[nCol-nStartCol] = rStr;
+            if (rStr.isEmpty())
+                bEmpty &= true;
             else
-            {
-                // Usually this is called for only a few positions of which
-                // most are not empty, so init from resource only if necessary.
-                OUString aColumn( ScGlobal::GetRscString(STR_COLUMN));
-                SetTableColumnName( maTableColumnNames, nCol-nStartCol, aColumn, nCol-nStartCol+1);
-            }
+                maTableColumnNames[nOff] = rStr;
+        }
+        if (bEmpty)
+        {
+            OUString aColumn( ScGlobal::GetRscString(STR_COLUMN));
+            SetTableColumnName( maTableColumnNames, nOff, aColumn, nOff+1);
         }
     }
+
+    if (aIntersection == aHeaderRange)
+        mbTableColumnNamesDirty = false;
 }
 
 sal_Int32 ScDBData::GetColumnNameOffset( const OUString& rName ) const
@@ -801,6 +909,24 @@ const OUString& ScDBData::GetTableColumnName( SCCOL nCol ) const
     return maTableColumnNames[nOffset];
 }
 
+void ScDBData::Notify( const SfxHint& rHint )
+{
+    const ScHint* pScHint = dynamic_cast<const ScHint*>(&rHint);
+    if (!pScHint)
+        return;
+
+    sal_uLong nHint = pScHint->GetId();
+    if (nHint & SC_HINT_DATACHANGED)
+    {
+        mbTableColumnNamesDirty = true;
+        if (mpContainer)
+            mpContainer->GetDirtyTableColumnNames().Join( pScHint->GetAddress());
+    }
+
+    // Do not refresh column names here, which might trigger unwanted
+    // recalculation.
+}
+
 namespace {
 
 class FindByTable : public unary_function<std::unique_ptr<ScDBData>, bool>
@@ -923,19 +1049,39 @@ public:
 
 }
 
+ScDocument* ScDBDataContainerBase::GetDocument() const
+{
+    return &mrDoc;
+}
+
+ScRangeList& ScDBDataContainerBase::GetDirtyTableColumnNames()
+{
+    return maDirtyTableColumnNames;
+}
+
 ScDBCollection::NamedDBs::NamedDBs(ScDBCollection& rParent, ScDocument& rDoc) :
-    mrParent(rParent), mrDoc(rDoc) {}
+    ScDBDataContainerBase(rDoc), mrParent(rParent) {}
 
 ScDBCollection::NamedDBs::NamedDBs(const NamedDBs& r)
-    : mrParent(r.mrParent)
-    , mrDoc(r.mrDoc)
+    : ScDBDataContainerBase(r.mrDoc)
+    , mrParent(r.mrParent)
 {
     for (auto const& it : r.m_DBs)
     {
-        m_DBs.insert(std::unique_ptr<ScDBData>(new ScDBData(*it)));
+        ScDBData* p = new ScDBData(*it);
+        std::unique_ptr<ScDBData> pData(p);
+        if (m_DBs.insert( std::move(pData)).second)
+        {
+            p->SetContainer( this);
+            p->StartTableColumnNamesListener(); // needs the container be set already
+        }
     }
 }
 
+ScDBCollection::NamedDBs::~NamedDBs()
+{
+}
+
 ScDBCollection::NamedDBs::iterator ScDBCollection::NamedDBs::begin()
 {
     return m_DBs.begin();
@@ -984,10 +1130,18 @@ bool ScDBCollection::NamedDBs::insert(ScDBData* p)
 
     pair<DBsType::iterator, bool> r = m_DBs.insert(std::move(pData));
 
-    if (r.second && p->HasImportParam() && !p->HasImportSelection())
+    if (r.second)
     {
-        p->SetRefreshHandler(mrParent.GetRefreshHandler());
-        p->SetRefreshControl(&mrDoc.GetRefreshTimerControlAddress());
+        p->SetContainer( this);
+        p->StartTableColumnNamesListener(); // needs the container be set already
+
+        /* TODO: shouldn't the import refresh not be setup for
+         * clipboard/undo documents? It was already like this before.. */
+        if (p->HasImportParam() && !p->HasImportSelection())
+        {
+            p->SetRefreshHandler(mrParent.GetRefreshHandler());
+            p->SetRefreshControl(&mrDoc.GetRefreshTimerControlAddress());
+        }
     }
     return r.second;
 }
@@ -1221,6 +1375,20 @@ ScDBData* ScDBCollection::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCO
     return NULL;
 }
 
+void ScDBCollection::RefreshDirtyTableColumnNames()
+{
+    for (size_t i=0; i < maNamedDBs.maDirtyTableColumnNames.size(); ++i)
+    {
+        const ScRange* pRange = maNamedDBs.maDirtyTableColumnNames[i];
+        for (auto const& it : maNamedDBs)
+        {
+            if (it->AreTableColumnNamesDirty())
+                it->RefreshTableColumnNames( &maNamedDBs.mrDoc, *pRange);
+        }
+    }
+    maNamedDBs.maDirtyTableColumnNames.RemoveAll();
+}
+
 void ScDBCollection::DeleteOnTab( SCTAB nTab )
 {
     FindByTable func(nTab);
diff --git a/sc/source/ui/docshell/docsh.cxx b/sc/source/ui/docshell/docsh.cxx
index c9c6b70..18add14 100644
--- a/sc/source/ui/docshell/docsh.cxx
+++ b/sc/source/ui/docshell/docsh.cxx
@@ -2800,6 +2800,7 @@ void ScDocShell::SetDocumentModified( bool bIsModified /* = true */ )
             aDocument.Broadcast(ScHint(SC_HINT_DATACHANGED, BCA_BRDCST_ALWAYS));
             if ( aDocument.IsForcedFormulaPending() && aDocument.GetAutoCalc() )
                 aDocument.CalcFormulaTree( true );
+            aDocument.RefreshDirtyTableColumnNames();
             PostDataChanged();
 
             //  Detective AutoUpdate:


More information about the Libreoffice-commits mailing list