sc/inc/refupdatecontext.hxx              |    1 
 sc/source/core/data/formulacell.cxx      |  266 ++++++++++++++++++++++++++++---
 sc/source/core/data/refupdatecontext.cxx |    6 
 3 files changed, 251 insertions(+), 22 deletions(-)

New commits:
commit d2a2d436aa10839c83a5471c9d37e2d69011825a
Author: Eike Rathke <er...@redhat.com>
Date:   Wed Mar 30 22:11:47 2016 +0200

    copy and adjust nested named expressions during sheet-copy
    
    Change-Id: Idec3d7f2282bb2521c1f46e4488bf85a47b19ea4

diff --git a/sc/inc/refupdatecontext.hxx b/sc/inc/refupdatecontext.hxx
index 272c3a2..2c47563 100644
--- a/sc/inc/refupdatecontext.hxx
+++ b/sc/inc/refupdatecontext.hxx
@@ -35,6 +35,7 @@ public:
     void setUpdatedName(SCTAB nTab, sal_uInt16 nIndex);
     bool isNameUpdated(SCTAB nTab, sal_uInt16 nIndex) const;
     NameIndicesType getUpdatedNames(SCTAB nTab) const;
+    bool isEmpty(SCTAB nTab) const;
 
 private:
     typedef std::unordered_map<SCTAB, NameIndicesType> UpdatedNamesType;
diff --git a/sc/source/core/data/formulacell.cxx 
b/sc/source/core/data/formulacell.cxx
index a8f8291..c3db7a9 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -420,9 +420,104 @@ bool lcl_isReference(const FormulaToken& rToken)
         rToken.GetType() == svDoubleRef;
 }
 
+enum MightReferenceSheet
+{
+    UNKNOWN,
+    NONE,
+    CODE,
+    NAME
+};
+
+MightReferenceSheet mightRangeNameReferenceSheet( ScRangeData* pData, SCTAB 
nRefTab)
+{
+    ScTokenArray* pCode = pData->GetCode();
+    if (!pCode)
+        return MightReferenceSheet::NONE;
+
+    for (const FormulaToken* p = pCode->First(); p; p = pCode->Next())
+    {
+        if (p->GetOpCode() == ocName)
+            return MightReferenceSheet::NAME;
+    }
+
+    return pCode->ReferencesSheet( nRefTab, pData->GetPos().Tab()) ?
+        MightReferenceSheet::CODE : MightReferenceSheet::NONE;
+}
+
+// See also lcl_FindRangeNamesInUse() below.
+/** Recursively find all named expressions that directly or indirectly (nested)
+    reference a given sheet.
+
+    @param  pToken
+            A token of type svIndex with OpCode ocName.
+ */
+bool findRangeNamesReferencingSheet( sc::UpdatedRangeNames& rIndexes, const 
FormulaToken* pToken,
+        const ScDocument* pDoc, SCTAB nGlobalRefTab, SCTAB nLocalRefTab,
+        SCTAB nOldTokenTab, SCTAB nOldTokenTabReplacement, int nRecursion)
+{
+    const sal_uInt16 nTokenIndex = pToken->GetIndex();
+    SCTAB nTokenTab = pToken->GetSheet();
+    if (nTokenTab < -1)
+    {
+        SAL_WARN("sc.core", "findRangeNamesReferencingSheet - nTokenTab < -1 : 
" <<
+                nTokenTab << ", nTokenIndex " << nTokenIndex << " Fix the 
creator!");
+#if OSL_DEBUG_LEVEL > 0
+        const ScRangeData* pData = pDoc->FindRangeNameByIndexAndSheet( 
nTokenIndex, nTokenTab);
+        SAL_WARN_IF( pData, "sc.core", "findRangeNamesReferencingSheet - named 
expression is: " << pData->GetName());
+#endif
+        nTokenTab = -1;
+    }
+    SCTAB nRefTab = nGlobalRefTab;
+    if (nTokenTab == nOldTokenTab)
+    {
+        nTokenTab = nOldTokenTabReplacement;
+        nRefTab = nLocalRefTab;
+    }
+    else if (nTokenTab == nOldTokenTabReplacement)
+    {
+        nRefTab = nLocalRefTab;
+    }
+
+    if (rIndexes.isNameUpdated( nTokenTab, nTokenIndex))
+        return true;
+
+    ScRangeData* pData = pDoc->FindRangeNameByIndexAndSheet( nTokenIndex, 
nTokenTab);
+    if (!pData)
+        return false;
+
+    ScTokenArray* pCode = pData->GetCode();
+    if (!pCode)
+        return false;
+
+    bool bRef = false;
+    if (nRecursion < 126)   // whatever.. 42*3
+    {
+        for (const FormulaToken* p = pCode->First(); p; p = pCode->Next())
+        {
+            if (p->GetOpCode() == ocName)
+            {
+                bRef |= findRangeNamesReferencingSheet( rIndexes, p, pDoc, 
nGlobalRefTab, nLocalRefTab,
+                        nOldTokenTab, nOldTokenTabReplacement, nRecursion+1);
+            }
+        }
+    }
+
+    if (!bRef)
+    {
+        SCTAB nPosTab = pData->GetPos().Tab();
+        if (nPosTab == nOldTokenTab)
+            nPosTab = nOldTokenTabReplacement;
+        bRef = pCode->ReferencesSheet( nRefTab, nPosTab);
+    }
+    if (bRef)
+        rIndexes.setUpdatedName( nTokenTab, nTokenIndex);
+
+    return bRef;
+}
+
 ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& 
rNewDoc, const ScDocument* pOldDoc,
         const ScAddress& rNewPos, const ScAddress& rOldPos, bool 
bGlobalNamesToLocal,
-        SCTAB nOldSheet, SCTAB & nNewSheet)
+        SCTAB nOldSheet, SCTAB & nNewSheet, bool bSameDoc)
 {
     ScAddress aRangePos( pOldRangeData->GetPos());
     if (nOldSheet < 0 && !bGlobalNamesToLocal)
@@ -437,7 +532,6 @@ ScRangeData* copyRangeName( const ScRangeData* 
pOldRangeData, ScDocument& rNewDo
     ScRangeData* pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc, 
&aRangePos);
     pRangeData->SetIndex(0);    // needed for insert to assign a new index
     ScTokenArray* pRangeNameToken = pRangeData->GetCode();
-    bool bSameDoc = (rNewDoc.GetPool() == 
const_cast<ScDocument*>(pOldDoc)->GetPool());
     if (bSameDoc && nNewSheet >= 0)
     {
         if (bGlobalNamesToLocal && nOldSheet < 0)
@@ -464,6 +558,80 @@ ScRangeData* copyRangeName( const ScRangeData* 
pOldRangeData, ScDocument& rNewDo
     return bInserted ? pRangeData : nullptr;
 }
 
+struct SheetIndex
+{
+    SCTAB       mnSheet;
+    sal_uInt16  mnIndex;
+
+    SheetIndex( SCTAB nSheet, sal_uInt16 nIndex ) : mnSheet(nSheet < -1 ? -1 : 
nSheet), mnIndex(nIndex) {}
+    bool operator<( const SheetIndex& r ) const
+    {
+        // Ascending order sheet, index
+        if (mnSheet < r.mnSheet)
+            return true;
+        if (mnSheet == r.mnSheet)
+            return mnIndex < r.mnIndex;
+        return false;
+    }
+};
+typedef std::map< SheetIndex, SheetIndex > SheetIndexMap;
+
+ScRangeData* copyRangeNames( SheetIndexMap& rSheetIndexMap, 
std::vector<ScRangeData*>& rRangeDataVec,
+        const sc::UpdatedRangeNames& rReferencingNames, SCTAB nTab,
+        const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const 
ScDocument* pOldDoc,
+        const ScAddress& rNewPos, const ScAddress& rOldPos, bool 
bGlobalNamesToLocal,
+        SCTAB nOldSheet, SCTAB & nNewSheet, bool bSameDoc)
+{
+    ScRangeData* pRangeData = nullptr;
+    const ScRangeName* pOldRangeName = (nTab < 0 ? pOldDoc->GetRangeName() : 
pOldDoc->GetRangeName(nTab));
+    if (pOldRangeName)
+    {
+        const ScRangeName* pNewRangeName = (nNewSheet < 0 ? 
rNewDoc.GetRangeName() : rNewDoc.GetRangeName(nNewSheet));
+        const SCTAB nFixSheet = nNewSheet;
+        sc::UpdatedRangeNames::NameIndicesType aSet( 
rReferencingNames.getUpdatedNames(nTab));
+        for (auto const & rIndex : aSet)
+        {
+            const ScRangeData* pCopyData = pOldRangeName->findByIndex(rIndex);
+            if (pCopyData)
+            {
+                // Match the original pOldRangeData to adapt the current
+                // token's values later. For that no check for an already
+                // copied name is needed as we only enter here if there was
+                // none.
+                if (pCopyData == pOldRangeData)
+                {
+                    pRangeData = copyRangeName( pCopyData, rNewDoc, pOldDoc, 
rNewPos, rOldPos,
+                            bGlobalNamesToLocal, nOldSheet, nNewSheet, 
bSameDoc);
+                    rRangeDataVec.push_back(pRangeData);
+                    rSheetIndexMap.insert( std::make_pair( SheetIndex( 
nOldSheet, pCopyData->GetIndex()),
+                                SheetIndex( nNewSheet, 
pRangeData->GetIndex())));
+                }
+                else
+                {
+                    // First check if the name is already available as copy.
+                    const ScRangeData* pFoundData = 
pNewRangeName->findByUpperName( pCopyData->GetUpperName());
+                    if (pFoundData)
+                    {
+                        // Just add the resulting sheet/index mapping.
+                        rSheetIndexMap.insert( std::make_pair( SheetIndex( 
nOldSheet, pCopyData->GetIndex()),
+                                    SheetIndex( nFixSheet, 
pFoundData->GetIndex())));
+                    }
+                    else
+                    {
+                        SCTAB nTmpSheet = nFixSheet;    // don't let the 
original get adapted
+                        ScRangeData* pTmpData = copyRangeName( pCopyData, 
rNewDoc, pOldDoc, rNewPos, rOldPos,
+                                bGlobalNamesToLocal, nOldSheet, nTmpSheet, 
bSameDoc);
+                        rRangeDataVec.push_back(pTmpData);
+                        rSheetIndexMap.insert( std::make_pair( SheetIndex( 
nOldSheet, pCopyData->GetIndex()),
+                                    SheetIndex( nTmpSheet, 
pTmpData->GetIndex())));
+                    }
+                }
+            }
+        }
+    }
+    return pRangeData;
+}
+
 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const 
ScDocument* pOldDoc,
         const ScAddress& rNewPos, const ScAddress& rOldPos, bool 
bGlobalNamesToLocal)
 {
@@ -482,15 +650,22 @@ void adjustRangeName(formula::FormulaToken* pToken, 
ScDocument& rNewDoc, const S
     int nOldIndex = pToken->GetIndex();
     ScRangeData* pOldRangeData = nullptr;
 
+    // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
+    bool bInsertingBefore = (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() 
<= rOldPos.Tab());
+
+    // The Tab where an old local name is to be found or that a global name
+    // references. May differ below from nOldSheet if a sheet was inserted
+    // before the old position. Global names and local names other than on the
+    // old sheet or new sheet are already updated, local names on the old sheet
+    // or inserted sheet will be updated later. Confusing stuff. Watch out.
+    SCTAB nOldTab = (nOldSheet < 0 ? rOldPos.Tab() : nOldSheet);
+    if (bInsertingBefore)
+        // Sheet was already inserted before old position.
+        ++nOldTab;
+
     // Search the name of the RangeName.
     if (nOldSheet >= 0)
     {
-        SCTAB nOldTab = nOldSheet;
-        // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
-        if (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab())
-            // Sheet was already inserted before old position.
-            ++nOldTab;
-
         const ScRangeName* pRangeName = pOldDoc->GetRangeName(nOldTab);
         pOldRangeData = pRangeName ? pRangeName->findByIndex(nOldIndex) : 
nullptr;
         if (!pOldRangeData)
@@ -526,24 +701,70 @@ void adjustRangeName(formula::FormulaToken* pToken, 
ScDocument& rNewDoc, const S
     // If no range name was found copy it.
     if (!pRangeData)
     {
-        if (nOldSheet < 0 && bGlobalNamesToLocal)
+        bool bCopyGlobalName = (nOldSheet < 0 && bGlobalNamesToLocal);
+        MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( 
pOldRangeData, nOldTab);
+        if (bCopyGlobalName && eMightReference == MightReferenceSheet::NONE)
+            return;
+
+        if (eMightReference == MightReferenceSheet::NAME)
         {
-            // Copy only global names to local that reference the old sheet.
-            SCTAB nOldTab = rOldPos.Tab();
-            if (bSameDoc && rNewPos.Tab() <= nOldTab)
+            // Name these to clarify what is passed where.
+            const SCTAB nGlobalRefTab = nOldTab;
+            const SCTAB nLocalRefTab = (bInsertingBefore ? nOldTab-1 : 
nOldTab);
+            const SCTAB nOldTokenTab = (nOldSheet < 0 ? (bInsertingBefore ? 
nOldTab-1 : nOldTab) : nOldSheet);
+            const SCTAB nOldTokenTabReplacement = nOldTab;
+            sc::UpdatedRangeNames aReferencingNames;
+            findRangeNamesReferencingSheet( aReferencingNames, pToken, pOldDoc,
+                    nGlobalRefTab, nLocalRefTab, nOldTokenTab, 
nOldTokenTabReplacement, 0);
+            if (bCopyGlobalName && aReferencingNames.isEmpty(-1) && 
aReferencingNames.isEmpty(nOldTokenTabReplacement))
+                return;
+
+            SheetIndexMap aSheetIndexMap;
+            std::vector<ScRangeData*> aRangeDataVec;
+            if (!aReferencingNames.isEmpty(nOldTokenTabReplacement))
             {
-                // Sheet was inserted before old position, references were
-                // already updated but rOldPos points to the old position,
-                // adjust to look for references.
-                ++nOldTab;
+                const SCTAB nTmpSheet = (nOldSheet < 0 ? nOldTab : nOldSheet);
+                pRangeData = copyRangeNames( aSheetIndexMap, aRangeDataVec, 
aReferencingNames, nOldTab,
+                        pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+                        bGlobalNamesToLocal, nTmpSheet, nNewSheet, bSameDoc);
+            }
+            if (bGlobalNamesToLocal && !aReferencingNames.isEmpty(-1))
+            {
+                const SCTAB nTmpSheet = -1;
+                ScRangeData* pTmpData = copyRangeNames( aSheetIndexMap, 
aRangeDataVec, aReferencingNames, -1,
+                        pOldRangeData, rNewDoc, pOldDoc, rNewPos, rOldPos,
+                        bGlobalNamesToLocal, nTmpSheet, nNewSheet, bSameDoc);
+                if (!pRangeData)
+                    pRangeData = pTmpData;
             }
-            if (!pOldRangeData->GetCode()->ReferencesSheet( nOldTab, 
pOldRangeData->GetPos().Tab()))
-                return;
-        }
 
-        // Also may modify nNewSheet to be set below at the end.
-        pRangeData = copyRangeName( pOldRangeData, rNewDoc, pOldDoc, rNewPos, 
rOldPos, bGlobalNamesToLocal,
-                nOldSheet, nNewSheet);
+            // Adjust copied nested names to new sheet/index.
+            for (auto & iRD : aRangeDataVec)
+            {
+                ScTokenArray* pCode = iRD->GetCode();
+                if (pCode)
+                {
+                    for (FormulaToken* p = pCode->First(); p; p = 
pCode->Next())
+                    {
+                        if (p->GetOpCode() == ocName)
+                        {
+                            auto it = aSheetIndexMap.find( SheetIndex( 
p->GetSheet(), p->GetIndex()));
+                            if (it != aSheetIndexMap.end())
+                            {
+                                p->SetSheet( it->second.mnSheet);
+                                p->SetIndex( it->second.mnIndex);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            // Also may modify nNewSheet to be set below at the end.
+            pRangeData = copyRangeName( pOldRangeData, rNewDoc, pOldDoc, 
rNewPos, rOldPos, bGlobalNamesToLocal,
+                    nOldSheet, nNewSheet, bSameDoc);
+        }
 
         if (!pRangeData)
         {
@@ -3752,6 +3973,7 @@ void ScFormulaCell::UpdateGrow( const ScRange& rArea, 
SCCOL nGrowX, SCROW nGrowY
         StartListeningTo( pDocument ); // Listener as previous
 }
 
+// See also findRangeNamesReferencingSheet() above.
 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, 
ScTokenArray* pCode, const ScDocument* pDoc,
         int nRecursion)
 {
diff --git a/sc/source/core/data/refupdatecontext.cxx 
b/sc/source/core/data/refupdatecontext.cxx
index 79a3792b..c065b35 100644
--- a/sc/source/core/data/refupdatecontext.cxx
+++ b/sc/source/core/data/refupdatecontext.cxx
@@ -56,6 +56,12 @@ UpdatedRangeNames::NameIndicesType 
UpdatedRangeNames::getUpdatedNames(SCTAB nTab
     return it->second;
 }
 
+bool UpdatedRangeNames::isEmpty(SCTAB nTab) const
+{
+    UpdatedNamesType::const_iterator it = maUpdatedNames.find(nTab);
+    return it == maUpdatedNames.end();
+}
+
 
 RefUpdateContext::RefUpdateContext(ScDocument& rDoc) :
     mrDoc(rDoc), meMode(URM_INSDEL), mnColDelta(0), mnRowDelta(0), 
mnTabDelta(0) {}
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to