LCOV - code coverage report
Current view: top level - sc/source/core/data - column4.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 613 735 83.4 %
Date: 2015-06-13 12:38:46 Functions: 72 80 90.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  */
       9             : 
      10             : #include <column.hxx>
      11             : #include <clipparam.hxx>
      12             : #include <cellvalue.hxx>
      13             : #include <attarray.hxx>
      14             : #include <document.hxx>
      15             : #include <cellvalues.hxx>
      16             : #include <columnspanset.hxx>
      17             : #include <listenercontext.hxx>
      18             : #include <tokenstringcontext.hxx>
      19             : #include <mtvcellfunc.hxx>
      20             : #include <clipcontext.hxx>
      21             : #include <attrib.hxx>
      22             : #include <patattr.hxx>
      23             : #include <docpool.hxx>
      24             : #include <conditio.hxx>
      25             : #include <formulagroup.hxx>
      26             : #include <tokenarray.hxx>
      27             : #include <globalnames.hxx>
      28             : #include <scitems.hxx>
      29             : #include <cellform.hxx>
      30             : #include <sharedformula.hxx>
      31             : 
      32             : #include <svl/sharedstringpool.hxx>
      33             : 
      34             : #include <vector>
      35             : #include <cassert>
      36             : 
      37             : #include <boost/shared_ptr.hpp>
      38             : 
      39          17 : bool ScColumn::IsMerged( SCROW nRow ) const
      40             : {
      41          17 :     return pAttrArray->IsMerged(nRow);
      42             : }
      43             : 
      44           4 : void ScColumn::DeleteBeforeCopyFromClip(
      45             :     sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
      46             : {
      47           4 :     sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
      48           4 :     if (!ValidRow(aRange.mnRow1) || !ValidRow(aRange.mnRow2))
      49           1 :         return;
      50             : 
      51           4 :     ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
      52           4 :     SCROW nClipRow1 = aClipRange.aStart.Row();
      53           4 :     SCROW nClipRow2 = aClipRange.aEnd.Row();
      54           4 :     SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1;
      55             : 
      56             :     // Check for non-empty cell ranges in the clip column.
      57           4 :     sc::SingleColumnSpanSet aSpanSet;
      58           4 :     aSpanSet.scan(rClipCol, nClipRow1, nClipRow2);
      59           7 :     sc::SingleColumnSpanSet::SpansType aSpans;
      60           4 :     aSpanSet.getSpans(aSpans);
      61             : 
      62           4 :     if (aSpans.empty())
      63             :         // All cells in the range in the clip are empty.  Nothing to delete.
      64           1 :         return;
      65             : 
      66             :     // Translate the clip column spans into the destination column, and repeat as needed.
      67           6 :     std::vector<sc::RowSpan> aDestSpans;
      68           3 :     SCROW nDestOffset = aRange.mnRow1 - nClipRow1;
      69           3 :     bool bContinue = true;
      70          12 :     while (bContinue)
      71             :     {
      72           6 :         sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
      73          14 :         for (; it != itEnd && bContinue; ++it)
      74             :         {
      75           8 :             const sc::RowSpan& r = *it;
      76           8 :             SCROW nDestRow1 = r.mnRow1 + nDestOffset;
      77           8 :             SCROW nDestRow2 = r.mnRow2 + nDestOffset;
      78             : 
      79           8 :             if (nDestRow1 > aRange.mnRow2)
      80             :             {
      81             :                 // We're done.
      82           3 :                 bContinue = false;
      83           3 :                 continue;
      84             :             }
      85             : 
      86           5 :             if (nDestRow2 > aRange.mnRow2)
      87             :             {
      88             :                 // Truncate this range, and set it as the last span.
      89           0 :                 nDestRow2 = aRange.mnRow2;
      90           0 :                 bContinue = false;
      91             :             }
      92             : 
      93           5 :             aDestSpans.push_back(sc::RowSpan(nDestRow1, nDestRow2));
      94             :         }
      95             : 
      96           6 :         nDestOffset += nClipRowLen;
      97             :     }
      98             : 
      99           3 :     InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
     100           3 :     sc::ColumnBlockPosition aBlockPos;
     101           3 :     InitBlockPosition(aBlockPos);
     102             : 
     103           3 :     std::vector<sc::RowSpan>::const_iterator it = aDestSpans.begin(), itEnd = aDestSpans.end();
     104           8 :     for (; it != itEnd; ++it)
     105             :     {
     106           5 :         SCROW nRow1 = it->mnRow1;
     107           5 :         SCROW nRow2 = it->mnRow2;
     108             : 
     109           5 :         if (nDelFlag & IDF_CONTENTS)
     110             :         {
     111           5 :             sc::SingleColumnSpanSet aDeletedRows;
     112           5 :             DeleteCells(aBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
     113           5 :             rBroadcastSpans.set(nTab, nCol, aDeletedRows, true);
     114             :         }
     115             : 
     116           5 :         if (nDelFlag & IDF_NOTE)
     117           5 :             DeleteCellNotes(aBlockPos, nRow1, nRow2);
     118             : 
     119           5 :         if (nDelFlag & IDF_EDITATTR)
     120           0 :             RemoveEditAttribs(nRow1, nRow2);
     121             : 
     122             :         // Delete attributes just now
     123           5 :         if (nDelFlag & IDF_ATTRIB)
     124             :         {
     125           5 :             pAttrArray->DeleteArea(nRow1, nRow2);
     126             : 
     127           5 :             if (rCxt.isTableProtected())
     128             :             {
     129           0 :                 ScPatternAttr aPattern(pDocument->GetPool());
     130           0 :                 aPattern.GetItemSet().Put(ScProtectionAttr(false));
     131           0 :                 ApplyPatternArea(nRow1, nRow2, aPattern);
     132             :             }
     133             : 
     134           5 :             ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
     135           5 :             if (pCondList)
     136           5 :                 pCondList->DeleteArea(nCol, nRow1, nCol, nRow2);
     137             :         }
     138           0 :         else if ((nDelFlag & IDF_HARDATTR) == IDF_HARDATTR)
     139           0 :             pAttrArray->DeleteHardAttr(nRow1, nRow2);
     140           3 :     }
     141             : }
     142             : 
     143          17 : void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
     144             : {
     145             :     assert(nRow1 <= nRow2);
     146             : 
     147          17 :     size_t nDestSize = nRow2 - nRow1 + 1;
     148          17 :     sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
     149          17 :     if (!pBlockPos)
     150          17 :         return;
     151             : 
     152          17 :     bool bSameDocPool = (rCxt.getClipDoc()->GetPool() == pDocument->GetPool());
     153             : 
     154          17 :     ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);
     155          17 :     sc::CellTextAttr& rSrcAttr = rCxt.getSingleCellAttr(nColOffset);
     156             : 
     157          17 :     InsertDeleteFlags nFlags = rCxt.getInsertFlag();
     158             : 
     159          17 :     if ((nFlags & IDF_ATTRIB) != IDF_NONE)
     160             :     {
     161          16 :         if (!rCxt.isSkipAttrForEmptyCells() || rSrcCell.meType != CELLTYPE_NONE)
     162             :         {
     163             :             const ScPatternAttr* pAttr = (bSameDocPool ? rCxt.getSingleCellPattern(nColOffset) :
     164          15 :                     rCxt.getSingleCellPattern(nColOffset)->PutInPool( pDocument, rCxt.getClipDoc()));
     165          15 :             pAttrArray->SetPatternArea(nRow1, nRow2, pAttr, true);
     166             :         }
     167             :     }
     168             : 
     169          17 :     if ((nFlags & IDF_CONTENTS) != IDF_NONE)
     170             :     {
     171          17 :         std::vector<sc::CellTextAttr> aTextAttrs(nDestSize, rSrcAttr);
     172             : 
     173          17 :         switch (rSrcCell.meType)
     174             :         {
     175             :             case CELLTYPE_VALUE:
     176             :             {
     177           2 :                 std::vector<double> aVals(nDestSize, rSrcCell.mfValue);
     178           4 :                 pBlockPos->miCellPos =
     179           2 :                     maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end());
     180           4 :                 pBlockPos->miCellTextAttrPos =
     181           2 :                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
     182           2 :                 CellStorageModified();
     183             :             }
     184           2 :             break;
     185             :             case CELLTYPE_STRING:
     186             :             {
     187             :                 // Compare the ScDocumentPool* to determine if we are copying within the
     188             :                 // same document. If not, re-intern shared strings.
     189           4 :                 svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? NULL : &pDocument->GetSharedStringPool());
     190             :                 svl::SharedString aStr = (pSharedStringPool ?
     191             :                         pSharedStringPool->intern( rSrcCell.mpString->getString()) :
     192           4 :                         *rSrcCell.mpString);
     193             : 
     194           8 :                 std::vector<svl::SharedString> aStrs(nDestSize, aStr);
     195           8 :                 pBlockPos->miCellPos =
     196           4 :                     maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
     197           8 :                 pBlockPos->miCellTextAttrPos =
     198           4 :                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
     199           8 :                 CellStorageModified();
     200             :             }
     201           4 :             break;
     202             :             case CELLTYPE_EDIT:
     203             :             {
     204           0 :                 std::vector<EditTextObject*> aStrs;
     205           0 :                 aStrs.reserve(nDestSize);
     206           0 :                 for (size_t i = 0; i < nDestSize; ++i)
     207           0 :                     aStrs.push_back(rSrcCell.mpEditText->Clone());
     208             : 
     209           0 :                 pBlockPos->miCellPos =
     210           0 :                     maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
     211           0 :                 pBlockPos->miCellTextAttrPos =
     212           0 :                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
     213           0 :                 CellStorageModified();
     214             :             }
     215           0 :             break;
     216             :             case CELLTYPE_FORMULA:
     217             :             {
     218           7 :                 std::vector<sc::RowSpan> aRanges;
     219           7 :                 aRanges.reserve(1);
     220           7 :                 aRanges.push_back(sc::RowSpan(nRow1, nRow2));
     221           7 :                 CloneFormulaCell(*rSrcCell.mpFormula, rSrcAttr, aRanges, NULL);
     222             :             }
     223           7 :             break;
     224             :             default:
     225             :                 ;
     226          17 :         }
     227             :     }
     228             : 
     229          17 :     const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
     230          17 :     if (pNote && (nFlags & (IDF_NOTE | IDF_ADDNOTES)) != IDF_NONE)
     231             :     {
     232             :         // Duplicate the cell note over the whole pasted range.
     233             : 
     234           3 :         ScDocument* pClipDoc = rCxt.getClipDoc();
     235           3 :         const ScAddress& rSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
     236           3 :         std::vector<ScPostIt*> aNotes;
     237           3 :         ScAddress aDestPos(nCol, nRow1, nTab);
     238           3 :         aNotes.reserve(nDestSize);
     239           6 :         for (size_t i = 0; i < nDestSize; ++i)
     240             :         {
     241           3 :             bool bCloneCaption = (nFlags & IDF_NOCAPTIONS) == IDF_NONE;
     242           3 :             aNotes.push_back(pNote->Clone(rSrcPos, *pDocument, aDestPos, bCloneCaption));
     243           3 :             aDestPos.IncRow();
     244             :         }
     245             : 
     246           6 :         pBlockPos->miCellNotePos =
     247             :             maCellNotes.set(
     248           6 :                 pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
     249             :     }
     250             : }
     251             : 
     252           0 : void ScColumn::SetValues( SCROW nRow, const std::vector<double>& rVals )
     253             : {
     254           0 :     if (!ValidRow(nRow))
     255           0 :         return;
     256             : 
     257           0 :     SCROW nLastRow = nRow + rVals.size() - 1;
     258           0 :     if (nLastRow > MAXROW)
     259             :         // Out of bound. Do nothing.
     260           0 :         return;
     261             : 
     262           0 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
     263           0 :     DetachFormulaCells(aPos, rVals.size());
     264             : 
     265           0 :     maCells.set(nRow, rVals.begin(), rVals.end());
     266           0 :     std::vector<sc::CellTextAttr> aDefaults(rVals.size());
     267           0 :     maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
     268             : 
     269           0 :     CellStorageModified();
     270             : 
     271           0 :     std::vector<SCROW> aRows;
     272           0 :     aRows.reserve(rVals.size());
     273           0 :     for (SCROW i = nRow; i <= nLastRow; ++i)
     274           0 :         aRows.push_back(i);
     275             : 
     276           0 :     BroadcastCells(aRows, SC_HINT_DATACHANGED);
     277             : }
     278             : 
     279           0 : void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest )
     280             : {
     281           0 :     if (!ValidRow(nRow))
     282           0 :         return;
     283             : 
     284           0 :     SCROW nLastRow = nRow + nLen - 1;
     285           0 :     if (nLastRow > MAXROW)
     286             :         // Out of bound. Do nothing.
     287           0 :         return;
     288             : 
     289           0 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
     290           0 :     DetachFormulaCells(aPos, nLen);
     291             : 
     292           0 :     rDest.transferFrom(*this, nRow, nLen);
     293             : 
     294           0 :     CellStorageModified();
     295             : 
     296           0 :     std::vector<SCROW> aRows;
     297           0 :     aRows.reserve(nLen);
     298           0 :     for (SCROW i = nRow; i <= nLastRow; ++i)
     299           0 :         aRows.push_back(i);
     300             : 
     301           0 :     BroadcastCells(aRows, SC_HINT_DATACHANGED);
     302             : }
     303             : 
     304           0 : void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
     305             : {
     306           0 :     if (!ValidRow(nRow))
     307           0 :         return;
     308             : 
     309           0 :     SCROW nLastRow = nRow + rSrc.size() - 1;
     310           0 :     if (nLastRow > MAXROW)
     311             :         // Out of bound. Do nothing
     312           0 :         return;
     313             : 
     314           0 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
     315           0 :     DetachFormulaCells(aPos, rSrc.size());
     316             : 
     317           0 :     rSrc.copyTo(*this, nRow);
     318             : 
     319           0 :     CellStorageModified();
     320             : 
     321           0 :     std::vector<SCROW> aRows;
     322           0 :     aRows.reserve(rSrc.size());
     323           0 :     for (SCROW i = nRow; i <= nLastRow; ++i)
     324           0 :         aRows.push_back(i);
     325             : 
     326           0 :     BroadcastCells(aRows, SC_HINT_DATACHANGED);
     327             : }
     328             : 
     329             : namespace {
     330             : 
     331           3 : class ConvertFormulaToValueHandler
     332             : {
     333             :     sc::CellValues maResValues;
     334             :     bool mbModified;
     335             : 
     336             : public:
     337           3 :     ConvertFormulaToValueHandler( SCTAB, SCCOL ) :
     338           3 :         mbModified(false)
     339             :     {
     340           3 :         maResValues.reset(MAXROWCOUNT);
     341           3 :     }
     342             : 
     343           6 :     void operator() ( size_t nRow, const ScFormulaCell* pCell )
     344             :     {
     345           6 :         sc::FormulaResultValue aRes = pCell->GetResult();
     346           6 :         switch (aRes.meType)
     347             :         {
     348             :             case sc::FormulaResultValue::Value:
     349           6 :                 maResValues.setValue(nRow, aRes.mfValue);
     350           6 :             break;
     351             :             case sc::FormulaResultValue::String:
     352           0 :                 maResValues.setValue(nRow, aRes.maString);
     353           0 :             break;
     354             :             case sc::FormulaResultValue::Error:
     355             :             case sc::FormulaResultValue::Invalid:
     356             :             default:
     357           0 :                 maResValues.setValue(nRow, svl::SharedString::getEmptyString());
     358             :         }
     359             : 
     360           6 :         mbModified = true;
     361           6 :     }
     362             : 
     363           3 :     bool isModified() const { return mbModified; }
     364             : 
     365           3 :     sc::CellValues& getResValues() { return maResValues; }
     366             : };
     367             : 
     368             : }
     369             : 
     370           3 : void ScColumn::ConvertFormulaToValue(
     371             :     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
     372             : {
     373           3 :     if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
     374           0 :         return;
     375             : 
     376           3 :     std::vector<SCROW> aBounds;
     377           3 :     aBounds.push_back(nRow1);
     378           3 :     if (nRow2 < MAXROW-1)
     379           3 :         aBounds.push_back(nRow2+1);
     380             : 
     381             :     // Split formula cell groups at top and bottom boundaries (if applicable).
     382           3 :     sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
     383             : 
     384             :     // Parse all formulas within the range and store their results into temporary storage.
     385           6 :     ConvertFormulaToValueHandler aFunc(nTab, nCol);
     386           3 :     sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
     387           3 :     if (!aFunc.isModified())
     388             :         // No formula cells encountered.
     389           0 :         return;
     390             : 
     391           3 :     DetachFormulaCells(rCxt, nRow1, nRow2);
     392             : 
     393             :     // Undo storage to hold static values which will get swapped to the cell storage later.
     394           6 :     sc::CellValues aUndoCells;
     395           3 :     aFunc.getResValues().swap(aUndoCells);
     396           3 :     aUndoCells.swapNonEmpty(*this);
     397           3 :     if (pUndo)
     398           6 :         pUndo->swap(nTab, nCol, aUndoCells);
     399             : }
     400             : 
     401             : namespace {
     402             : 
     403             : class StartListeningHandler
     404             : {
     405             :     sc::StartListeningContext& mrCxt;
     406             : 
     407             : public:
     408           7 :     StartListeningHandler( sc::StartListeningContext& rCxt ) :
     409           7 :         mrCxt(rCxt) {}
     410             : 
     411          10 :     void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
     412             :     {
     413          10 :         pCell->StartListeningTo(mrCxt);
     414          10 :     }
     415             : };
     416             : 
     417             : class EndListeningHandler
     418             : {
     419             :     sc::EndListeningContext& mrCxt;
     420             : 
     421             : public:
     422           7 :     EndListeningHandler( sc::EndListeningContext& rCxt ) :
     423           7 :         mrCxt(rCxt) {}
     424             : 
     425           4 :     void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
     426             :     {
     427           4 :         pCell->EndListeningTo(mrCxt);
     428           4 :     }
     429             : };
     430             : 
     431             : }
     432             : 
     433           7 : void ScColumn::SwapNonEmpty(
     434             :     sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
     435             : {
     436           7 :     const ScRange& rRange = rValues.getRange();
     437           7 :     std::vector<SCROW> aBounds;
     438           7 :     aBounds.push_back(rRange.aStart.Row());
     439           7 :     if (rRange.aEnd.Row() < MAXROW-1)
     440           7 :         aBounds.push_back(rRange.aEnd.Row()+1);
     441             : 
     442             :     // Split formula cell groups at top and bottom boundaries (if applicable).
     443           7 :     sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
     444          14 :     std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
     445             : 
     446             :     // Detach formula cells within the spans (if any).
     447           7 :     EndListeningHandler aEndLisFunc(rEndCxt);
     448           7 :     std::vector<sc::CellValueSpan>::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
     449           7 :     sc::CellStoreType::iterator itPos = maCells.begin();
     450          15 :     for (; it != itEnd; ++it)
     451             :     {
     452           8 :         SCROW nRow1 = it->mnRow1;
     453           8 :         SCROW nRow2 = it->mnRow2;
     454           8 :         itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
     455             :     }
     456             : 
     457           7 :     rValues.swapNonEmpty(nTab, nCol, *this);
     458           7 :     RegroupFormulaCells();
     459             : 
     460             :     // Attach formula cells within the spans (if any).
     461           7 :     StartListeningHandler aStartLisFunc(rStartCxt);
     462           7 :     it = aSpans.begin();
     463           7 :     itPos = maCells.begin();
     464          15 :     for (; it != itEnd; ++it)
     465             :     {
     466           8 :         SCROW nRow1 = it->mnRow1;
     467           8 :         SCROW nRow2 = it->mnRow2;
     468           8 :         itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
     469             :     }
     470             : 
     471          14 :     CellStorageModified();
     472           7 : }
     473             : 
     474           1 : void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag, bool bBroadcast )
     475             : {
     476           1 :     std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
     477           2 :     for (; itSpan != itSpanEnd; ++itSpan)
     478           1 :         DeleteArea(itSpan->mnRow1, itSpan->mnRow2, nDelFlag, bBroadcast);
     479           1 : }
     480             : 
     481           8 : void ScColumn::CloneFormulaCell(
     482             :     const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
     483             :     const std::vector<sc::RowSpan>& rRanges, sc::StartListeningContext* pCxt )
     484             : {
     485           8 :     sc::CellStoreType::iterator itPos = maCells.begin();
     486           8 :     sc::CellTextAttrStoreType::iterator itAttrPos = maCellTextAttrs.begin();
     487             : 
     488           8 :     std::vector<ScFormulaCell*> aFormulas;
     489           8 :     std::vector<sc::RowSpan>::const_iterator itSpan = rRanges.begin(), itSpanEnd = rRanges.end();
     490          16 :     for (; itSpan != itSpanEnd; ++itSpan)
     491             :     {
     492           8 :         SCROW nRow1 = itSpan->mnRow1, nRow2 = itSpan->mnRow2;
     493           8 :         size_t nLen = nRow2 - nRow1 + 1;
     494             :         assert(nLen > 0);
     495           8 :         aFormulas.clear();
     496           8 :         aFormulas.reserve(nLen);
     497             : 
     498           8 :         ScAddress aPos(nCol, nRow1, nTab);
     499             : 
     500           8 :         if (nLen == 1)
     501             :         {
     502             :             // Single, ungrouped formula cell.
     503           5 :             ScFormulaCell* pCell = new ScFormulaCell(rSrc, *pDocument, aPos);
     504           5 :             if (pCxt)
     505             :             {
     506           0 :                 pCell->StartListeningTo(*pCxt);
     507           0 :                 pCell->SetDirty();
     508             :             }
     509           5 :             aFormulas.push_back(pCell);
     510             :         }
     511             :         else
     512             :         {
     513             :             // Create a group of formula cells.
     514           3 :             ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
     515           3 :             xGroup->setCode(*rSrc.GetCode());
     516           3 :             xGroup->compileCode(*pDocument, aPos, pDocument->GetGrammar());
     517          25 :             for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
     518             :             {
     519          22 :                 ScFormulaCell* pCell = new ScFormulaCell(pDocument, aPos, xGroup, pDocument->GetGrammar(), rSrc.GetMatrixFlag());
     520          22 :                 if (i == 0)
     521             :                 {
     522           3 :                     xGroup->mpTopCell = pCell;
     523           3 :                     xGroup->mnLength = nLen;
     524             :                 }
     525          22 :                 if (pCxt)
     526             :                 {
     527           0 :                     pCell->StartListeningTo(*pCxt);
     528           0 :                     pCell->SetDirty();
     529             :                 }
     530          22 :                 aFormulas.push_back(pCell);
     531           3 :             }
     532             :         }
     533             : 
     534           8 :         itPos = maCells.set(itPos, nRow1, aFormulas.begin(), aFormulas.end());
     535             : 
     536             :         // Join the top and bottom of the pasted formula cells as needed.
     537           8 :         sc::CellStoreType::position_type aPosObj = maCells.position(itPos, nRow1);
     538             : 
     539             :         assert(aPosObj.first->type == sc::element_type_formula);
     540           8 :         ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
     541           8 :         JoinNewFormulaCell(aPosObj, *pCell);
     542             : 
     543           8 :         aPosObj = maCells.position(aPosObj.first, nRow2);
     544             :         assert(aPosObj.first->type == sc::element_type_formula);
     545           8 :         pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
     546           8 :         JoinNewFormulaCell(aPosObj, *pCell);
     547             : 
     548           8 :         std::vector<sc::CellTextAttr> aTextAttrs(nLen, rAttr);
     549           8 :         itAttrPos = maCellTextAttrs.set(itAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
     550           8 :     }
     551             : 
     552           8 :     CellStorageModified();
     553           8 : }
     554             : 
     555          21 : ScPostIt* ScColumn::ReleaseNote( SCROW nRow )
     556             : {
     557          21 :     if (!ValidRow(nRow))
     558           0 :         return NULL;
     559             : 
     560          21 :     ScPostIt* p = NULL;
     561          21 :     maCellNotes.release(nRow, p);
     562          21 :     return p;
     563             : }
     564             : 
     565        1056 : size_t ScColumn::GetNoteCount() const
     566             : {
     567        1056 :     size_t nCount = 0;
     568        1056 :     sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
     569        2146 :     for (; it != itEnd; ++it)
     570             :     {
     571        1090 :         if (it->type != sc::element_type_cellnote)
     572        1073 :             continue;
     573             : 
     574          17 :         nCount += it->size;
     575             :     }
     576             : 
     577        1056 :     return nCount;
     578             : }
     579             : 
     580             : namespace {
     581             : 
     582             : class NoteCaptionCreator
     583             : {
     584             :     ScAddress maPos;
     585             : public:
     586       34816 :     NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {}
     587             : 
     588           4 :     void operator() ( size_t nRow, ScPostIt* p )
     589             :     {
     590           4 :         maPos.SetRow(nRow);
     591           4 :         p->GetOrCreateCaption(maPos);
     592           4 :     }
     593             : };
     594             : 
     595             : struct NoteCaptionCleaner
     596             : {
     597          10 :     void operator() ( size_t /*nRow*/, ScPostIt* p )
     598             :     {
     599          10 :         p->ForgetCaption();
     600          10 :     }
     601             : };
     602             : 
     603             : }
     604             : 
     605       34816 : void ScColumn::CreateAllNoteCaptions()
     606             : {
     607       34816 :     NoteCaptionCreator aFunc(nTab, nCol);
     608       34816 :     sc::ProcessNote(maCellNotes, aFunc);
     609       34816 : }
     610             : 
     611          16 : void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2 )
     612             : {
     613          16 :     if (!ValidRow(nRow1) || !ValidRow(nRow2))
     614          16 :         return;
     615             : 
     616             :     NoteCaptionCleaner aFunc;
     617          16 :     sc::CellNoteStoreType::iterator it = maCellNotes.begin();
     618          16 :     sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc);
     619             : }
     620             : 
     621           6 : SCROW ScColumn::GetNotePosition( size_t nIndex ) const
     622             : {
     623             :     // Return the row position of the nth note in the column.
     624             : 
     625           6 :     sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
     626             : 
     627           6 :     size_t nCount = 0; // Number of notes encountered so far.
     628          12 :     for (; it != itEnd; ++it)
     629             :     {
     630          12 :         if (it->type != sc::element_type_cellnote)
     631             :             // Skip the empty blocks.
     632           6 :             continue;
     633             : 
     634           6 :         if (nIndex < nCount + it->size)
     635             :         {
     636             :             // Index falls within this block.
     637           6 :             size_t nOffset = nIndex - nCount;
     638           6 :             return it->position + nOffset;
     639             :         }
     640             : 
     641           0 :         nCount += it->size;
     642             :     }
     643             : 
     644           0 :     return -1;
     645             : }
     646             : 
     647             : namespace {
     648             : 
     649             : class NoteEntryCollector
     650             : {
     651             :     std::vector<sc::NoteEntry>& mrNotes;
     652             :     SCTAB mnTab;
     653             :     SCCOL mnCol;
     654             :     SCROW mnStartRow;
     655             :     SCROW mnEndRow;
     656             : public:
     657      314368 :     NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol,
     658             :             SCROW nStartRow = 0, SCROW nEndRow = MAXROW) :
     659             :         mrNotes(rNotes), mnTab(nTab), mnCol(nCol),
     660      314368 :         mnStartRow(nStartRow), mnEndRow(nEndRow) {}
     661             : 
     662      302145 :     void operator() (const sc::CellNoteStoreType::value_type& node) const
     663             :     {
     664      302145 :         if (node.type != sc::element_type_cellnote)
     665      604256 :             return;
     666             : 
     667          34 :         size_t nTopRow = node.position;
     668          34 :         sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data);
     669          34 :         sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data);
     670          34 :         size_t nOffset = 0;
     671          34 :         if(nTopRow < size_t(mnStartRow))
     672             :         {
     673           0 :             std::advance(it, mnStartRow - nTopRow);
     674           0 :             nOffset = mnStartRow - nTopRow;
     675             :         }
     676             : 
     677          68 :         for (; it != itEnd && nTopRow + nOffset <= size_t(mnEndRow);
     678             :                 ++it, ++nOffset)
     679             :         {
     680          34 :             ScAddress aPos(mnCol, nTopRow + nOffset, mnTab);
     681          34 :             mrNotes.push_back(sc::NoteEntry(aPos, *it));
     682             :         }
     683             :     }
     684             : };
     685             : 
     686             : }
     687             : 
     688      302080 : void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
     689             : {
     690      302080 :     std::for_each(maCellNotes.begin(), maCellNotes.end(), NoteEntryCollector(rNotes, nTab, nCol));
     691      302080 : }
     692             : 
     693       12288 : void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow,
     694             :         std::vector<sc::NoteEntry>& rNotes ) const
     695             : {
     696       12288 :     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
     697       12288 :     sc::CellNoteStoreType::const_iterator it = aPos.first;
     698       12288 :     if (it == maCellNotes.end())
     699             :         // Invalid row number.
     700       12288 :         return;
     701             : 
     702             :     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
     703       12288 :         maCellNotes.position(nEndRow);
     704       12288 :     sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first;
     705             : 
     706       12288 :     std::for_each(it, itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow));
     707             : }
     708             : 
     709             : namespace {
     710             : 
     711             : class RecompileByOpcodeHandler
     712             : {
     713             :     ScDocument* mpDoc;
     714             :     const formula::unordered_opcode_set& mrOps;
     715             :     sc::EndListeningContext& mrEndListenCxt;
     716             :     sc::CompileFormulaContext& mrCompileFormulaCxt;
     717             : 
     718             : public:
     719       82944 :     RecompileByOpcodeHandler(
     720             :         ScDocument* pDoc, const formula::unordered_opcode_set& rOps,
     721             :         sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
     722             :         mpDoc(pDoc),
     723             :         mrOps(rOps),
     724             :         mrEndListenCxt(rEndListenCxt),
     725       82944 :         mrCompileFormulaCxt(rCompileCxt) {}
     726             : 
     727          14 :     void operator() ( sc::FormulaGroupEntry& rEntry )
     728             :     {
     729             :         // Perform end listening, remove from formula tree, and set them up
     730             :         // for re-compilation.
     731             : 
     732          14 :         ScFormulaCell* pTop = NULL;
     733             : 
     734          14 :         if (rEntry.mbShared)
     735             :         {
     736             :             // Only inspect the code from the top cell.
     737           5 :             pTop = *rEntry.mpCells;
     738             :         }
     739             :         else
     740           9 :             pTop = rEntry.mpCell;
     741             : 
     742          14 :         ScTokenArray* pCode = pTop->GetCode();
     743          14 :         bool bRecompile = pCode->HasOpCodes(mrOps);
     744             : 
     745          14 :         if (bRecompile)
     746             :         {
     747             :             // Get the formula string.
     748           5 :             OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
     749           5 :             sal_Int32 n = aFormula.getLength();
     750           5 :             if (pTop->GetMatrixFlag() != MM_NONE && n > 0)
     751             :             {
     752           0 :                 if (aFormula[0] == '{' && aFormula[n-1] == '}')
     753           0 :                     aFormula = aFormula.copy(1, n-2);
     754             :             }
     755             : 
     756           5 :             if (rEntry.mbShared)
     757             :             {
     758           4 :                 ScFormulaCell** pp = rEntry.mpCells;
     759           4 :                 ScFormulaCell** ppEnd = pp + rEntry.mnLength;
     760          16 :                 for (; pp != ppEnd; ++pp)
     761             :                 {
     762          12 :                     ScFormulaCell* p = *pp;
     763          12 :                     p->EndListeningTo(mrEndListenCxt);
     764          12 :                     mpDoc->RemoveFromFormulaTree(p);
     765             :                 }
     766             :             }
     767             :             else
     768             :             {
     769           1 :                 rEntry.mpCell->EndListeningTo(mrEndListenCxt);
     770           1 :                 mpDoc->RemoveFromFormulaTree(rEntry.mpCell);
     771             :             }
     772             : 
     773           5 :             pCode->Clear();
     774           5 :             pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar());
     775             :         }
     776          14 :     }
     777             : };
     778             : 
     779             : class CompileHybridFormulaHandler
     780             : {
     781             :     ScDocument* mpDoc;
     782             :     sc::StartListeningContext& mrStartListenCxt;
     783             :     sc::CompileFormulaContext& mrCompileFormulaCxt;
     784             : 
     785             : public:
     786       82944 :     CompileHybridFormulaHandler( ScDocument* pDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
     787             :         mpDoc(pDoc),
     788             :         mrStartListenCxt(rStartListenCxt),
     789       82944 :         mrCompileFormulaCxt(rCompileCxt) {}
     790             : 
     791          14 :     void operator() ( sc::FormulaGroupEntry& rEntry )
     792             :     {
     793          14 :         if (rEntry.mbShared)
     794             :         {
     795           5 :             ScFormulaCell* pTop = *rEntry.mpCells;
     796           5 :             OUString aFormula = pTop->GetHybridFormula();
     797             : 
     798           5 :             if (!aFormula.isEmpty())
     799             :             {
     800             :                 // Create a new token array from the hybrid formula string, and
     801             :                 // set it to the group.
     802           4 :                 ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
     803           4 :                 ScTokenArray* pNewCode = aComp.CompileString(aFormula);
     804           8 :                 ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
     805             :                 assert(xGroup);
     806           4 :                 xGroup->setCode(pNewCode);
     807           4 :                 xGroup->compileCode(*mpDoc, pTop->aPos, mpDoc->GetGrammar());
     808             : 
     809             :                 // Propagate the new token array to all formula cells in the group.
     810           4 :                 ScFormulaCell** pp = rEntry.mpCells;
     811           4 :                 ScFormulaCell** ppEnd = pp + rEntry.mnLength;
     812          16 :                 for (; pp != ppEnd; ++pp)
     813             :                 {
     814          12 :                     ScFormulaCell* p = *pp;
     815          12 :                     p->SyncSharedCode();
     816          12 :                     p->StartListeningTo(mrStartListenCxt);
     817          12 :                     p->SetDirty();
     818           4 :                 }
     819           5 :             }
     820             :         }
     821             :         else
     822             :         {
     823           9 :             ScFormulaCell* pCell = rEntry.mpCell;
     824           9 :             OUString aFormula = pCell->GetHybridFormula();
     825             : 
     826           9 :             if (!aFormula.isEmpty())
     827             :             {
     828             :                 // Create token array from formula string.
     829           1 :                 ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
     830           1 :                 ScTokenArray* pNewCode = aComp.CompileString(aFormula);
     831             : 
     832             :                 // Generate RPN tokens.
     833           2 :                 ScCompiler aComp2(mpDoc, pCell->aPos, *pNewCode);
     834           1 :                 aComp2.CompileTokenArray();
     835             : 
     836           1 :                 pCell->SetCode(pNewCode);
     837           1 :                 pCell->StartListeningTo(mrStartListenCxt);
     838           2 :                 pCell->SetDirty();
     839           9 :             }
     840             :         }
     841          14 :     }
     842             : };
     843             : 
     844             : }
     845             : 
     846       71680 : void ScColumn::PreprocessRangeNameUpdate(
     847             :     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
     848             : {
     849             :     // Collect all formula groups.
     850       71680 :     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
     851             : 
     852      143360 :     formula::unordered_opcode_set aOps;
     853       71680 :     aOps.insert(ocBad);
     854       71680 :     aOps.insert(ocColRowName);
     855       71680 :     aOps.insert(ocName);
     856       71680 :     RecompileByOpcodeHandler aFunc(pDocument, aOps, rEndListenCxt, rCompileCxt);
     857      143360 :     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
     858       71680 : }
     859             : 
     860       11264 : void ScColumn::PreprocessDBDataUpdate(
     861             :     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
     862             : {
     863             :     // Collect all formula groups.
     864       11264 :     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
     865             : 
     866       22528 :     formula::unordered_opcode_set aOps;
     867       11264 :     aOps.insert(ocBad);
     868       11264 :     aOps.insert(ocColRowName);
     869       11264 :     aOps.insert(ocDBArea);
     870       11264 :     aOps.insert(ocTableRef);
     871       11264 :     RecompileByOpcodeHandler aFunc(pDocument, aOps, rEndListenCxt, rCompileCxt);
     872       22528 :     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
     873       11264 : }
     874             : 
     875       82944 : void ScColumn::CompileHybridFormula(
     876             :     sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
     877             : {
     878             :     // Collect all formula groups.
     879       82944 :     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
     880             : 
     881       82944 :     CompileHybridFormulaHandler aFunc(pDocument, rStartListenCxt, rCompileCxt);
     882       82944 :     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
     883       82944 : }
     884             : 
     885             : namespace {
     886             : 
     887             : class ScriptTypeUpdater
     888             : {
     889             :     ScColumn& mrCol;
     890             :     sc::CellTextAttrStoreType& mrTextAttrs;
     891             :     sc::CellTextAttrStoreType::iterator miPosAttr;
     892             :     ScConditionalFormatList* mpCFList;
     893             :     SvNumberFormatter* mpFormatter;
     894             :     ScAddress maPos;
     895             :     bool mbUpdated;
     896             : 
     897             : private:
     898         560 :     void updateScriptType( size_t nRow, ScRefCellValue& rCell )
     899             :     {
     900         560 :         sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
     901         560 :         miPosAttr = aAttrPos.first;
     902             : 
     903         560 :         if (aAttrPos.first->type != sc::element_type_celltextattr)
     904         180 :             return;
     905             : 
     906         560 :         sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
     907         560 :         if (rAttr.mnScriptType != SvtScriptType::UNKNOWN)
     908             :             // Script type already deteremined.  Skip it.
     909         180 :             return;
     910             : 
     911         380 :         const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
     912         380 :         if (!pPat)
     913             :             // In theory this should never return NULL. But let's be safe.
     914           0 :             return;
     915             : 
     916         380 :         const SfxItemSet* pCondSet = NULL;
     917         380 :         if (mpCFList)
     918             :         {
     919         380 :             maPos.SetRow(nRow);
     920             :             const ScCondFormatItem& rItem =
     921         380 :                 static_cast<const ScCondFormatItem&>(pPat->GetItem(ATTR_CONDITIONAL));
     922         380 :             const std::vector<sal_uInt32>& rData = rItem.GetCondFormatData();
     923         380 :             pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
     924             :         }
     925             : 
     926         380 :         OUString aStr;
     927             :         Color* pColor;
     928         380 :         sal_uLong nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
     929         380 :         ScCellFormat::GetString(rCell, nFormat, aStr, &pColor, *mpFormatter, &mrCol.GetDoc());
     930             : 
     931         380 :         rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
     932         380 :         mbUpdated = true;
     933             :     }
     934             : 
     935             : public:
     936       17504 :     ScriptTypeUpdater( ScColumn& rCol ) :
     937             :         mrCol(rCol),
     938       17504 :         mrTextAttrs(rCol.GetCellAttrStore()),
     939             :         miPosAttr(mrTextAttrs.begin()),
     940       17504 :         mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
     941       17504 :         mpFormatter(rCol.GetDoc().GetFormatTable()),
     942       35008 :         maPos(rCol.GetCol(), 0, rCol.GetTab()),
     943      105024 :         mbUpdated(false)
     944       17504 :     {}
     945             : 
     946         250 :     void operator() ( size_t nRow, double fVal )
     947             :     {
     948         250 :         ScRefCellValue aCell(fVal);
     949         250 :         updateScriptType(nRow, aCell);
     950         250 :     }
     951             : 
     952         128 :     void operator() ( size_t nRow, const svl::SharedString& rStr )
     953             :     {
     954         128 :         ScRefCellValue aCell(&rStr);
     955         128 :         updateScriptType(nRow, aCell);
     956         128 :     }
     957             : 
     958           0 :     void operator() ( size_t nRow, const EditTextObject* pText )
     959             :     {
     960           0 :         ScRefCellValue aCell(pText);
     961           0 :         updateScriptType(nRow, aCell);
     962           0 :     }
     963             : 
     964         182 :     void operator() ( size_t nRow, const ScFormulaCell* pCell )
     965             :     {
     966         182 :         ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
     967         182 :         updateScriptType(nRow, aCell);
     968         182 :     }
     969             : 
     970       17504 :     bool isUpdated() const { return mbUpdated; }
     971             : };
     972             : 
     973             : }
     974             : 
     975       17504 : void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
     976             : {
     977       17504 :     if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
     978       17504 :         return;
     979             : 
     980       17504 :     ScriptTypeUpdater aFunc(*this);
     981       17504 :     sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
     982       17504 :     if (aFunc.isUpdated())
     983         142 :         CellStorageModified();
     984             : }
     985             : 
     986          18 : void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
     987             : {
     988          18 :     maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
     989          18 :     maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
     990          18 :     maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
     991          18 :     maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
     992             : 
     993          18 :     if (bPattern)
     994             :     {
     995          43 :         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
     996             :         {
     997          25 :             const ScPatternAttr* pPat1 = GetPattern(nRow);
     998          25 :             const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
     999          25 :             if (pPat1 != pPat2)
    1000             :             {
    1001           0 :                 SetPattern(nRow, *pPat2, true);
    1002           0 :                 rOther.SetPattern(nRow, *pPat1, true);
    1003             :             }
    1004             :         }
    1005             :     }
    1006             : 
    1007          18 :     CellStorageModified();
    1008          18 :     rOther.CellStorageModified();
    1009          18 : }
    1010             : 
    1011             : namespace {
    1012             : 
    1013             : class FormulaColPosSetter
    1014             : {
    1015             :     SCCOL mnCol;
    1016             :     bool  mbUpdateRefs;
    1017             : public:
    1018          38 :     FormulaColPosSetter( SCCOL nCol, bool bUpdateRefs ) : mnCol(nCol), mbUpdateRefs(bUpdateRefs) {}
    1019             : 
    1020           3 :     void operator() ( size_t nRow, ScFormulaCell* pCell )
    1021             :     {
    1022           3 :         if (!pCell->IsShared() || pCell->IsSharedTop())
    1023             :         {
    1024             :             // Ensure that the references still point to the same locations
    1025             :             // after the position change.
    1026           1 :             ScAddress aOldPos = pCell->aPos;
    1027           1 :             pCell->aPos.SetCol(mnCol);
    1028           1 :             pCell->aPos.SetRow(nRow);
    1029           1 :             if (mbUpdateRefs)
    1030           1 :                 pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
    1031             :             else
    1032           0 :                 pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos);
    1033             :         }
    1034             :         else
    1035             :         {
    1036           2 :             pCell->aPos.SetCol(mnCol);
    1037           2 :             pCell->aPos.SetRow(nRow);
    1038             :         }
    1039           3 :     }
    1040             : };
    1041             : 
    1042             : }
    1043             : 
    1044          38 : void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2, bool bUpdateRefs )
    1045             : {
    1046          38 :     FormulaColPosSetter aFunc(nCol, bUpdateRefs);
    1047          38 :     sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
    1048          38 : }
    1049             : 
    1050             : namespace {
    1051             : 
    1052          38 : class RelativeRefBoundChecker
    1053             : {
    1054             :     std::vector<SCROW> maBounds;
    1055             :     ScRange maBoundRange;
    1056             : 
    1057             : public:
    1058          38 :     RelativeRefBoundChecker( const ScRange& rBoundRange ) :
    1059          38 :         maBoundRange(rBoundRange) {}
    1060             : 
    1061           3 :     void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
    1062             :     {
    1063           3 :         if (!pCell->IsSharedTop())
    1064           5 :             return;
    1065             : 
    1066             :         pCell->GetCode()->CheckRelativeReferenceBounds(
    1067           1 :             pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
    1068             :     }
    1069             : 
    1070          38 :     void swapBounds( std::vector<SCROW>& rBounds )
    1071             :     {
    1072          38 :         rBounds.swap(maBounds);
    1073          38 :     }
    1074             : };
    1075             : 
    1076             : }
    1077             : 
    1078          38 : void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
    1079             : {
    1080          38 :     if (rBoundRange.aStart.Row() >= MAXROW)
    1081             :         // Nothing to split.
    1082          38 :         return;
    1083             : 
    1084          38 :     std::vector<SCROW> aBounds;
    1085             : 
    1086             :     // Cut at row boundaries first.
    1087          38 :     aBounds.push_back(rBoundRange.aStart.Row());
    1088          38 :     if (rBoundRange.aEnd.Row() < MAXROW)
    1089          38 :         aBounds.push_back(rBoundRange.aEnd.Row()+1);
    1090          38 :     sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
    1091             : 
    1092          76 :     RelativeRefBoundChecker aFunc(rBoundRange);
    1093             :     sc::ProcessFormula(
    1094          38 :         maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
    1095          38 :     aFunc.swapBounds(aBounds);
    1096          76 :     sc::SharedFormulaUtil::splitFormulaCellGroups(maCells, aBounds);
    1097             : }
    1098             : 
    1099             : namespace {
    1100             : 
    1101             : class ListenerCollector
    1102             : {
    1103             :     std::vector<SvtListener*>& mrListeners;
    1104             : public:
    1105         158 :     ListenerCollector( std::vector<SvtListener*>& rListener ) :
    1106         158 :         mrListeners(rListener) {}
    1107             : 
    1108         141 :     void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
    1109             :     {
    1110         141 :         SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
    1111         141 :         std::copy(rLis.begin(), rLis.end(), std::back_inserter(mrListeners));
    1112         141 :     }
    1113             : };
    1114             : 
    1115             : class FormulaCellCollector
    1116             : {
    1117             :     std::vector<ScFormulaCell*>& mrCells;
    1118             : public:
    1119           4 :     FormulaCellCollector( std::vector<ScFormulaCell*>& rCells ) : mrCells(rCells) {}
    1120             : 
    1121          20 :     void operator() ( size_t /*nRow*/, ScFormulaCell* p )
    1122             :     {
    1123          20 :         mrCells.push_back(p);
    1124          20 :     }
    1125             : };
    1126             : 
    1127             : }
    1128             : 
    1129         158 : void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
    1130             : {
    1131         158 :     if (nRow2 < nRow1 || !ValidRow(nRow1) || !ValidRow(nRow2))
    1132         158 :         return;
    1133             : 
    1134         158 :     ListenerCollector aFunc(rListeners);
    1135         158 :     sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
    1136             : }
    1137             : 
    1138           4 : void ScColumn::CollectFormulaCells( std::vector<ScFormulaCell*>& rCells, SCROW nRow1, SCROW nRow2 )
    1139             : {
    1140           4 :     if (nRow2 < nRow1 || !ValidRow(nRow1) || !ValidRow(nRow2))
    1141           4 :         return;
    1142             : 
    1143           4 :     FormulaCellCollector aFunc(rCells);
    1144           4 :     sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
    1145             : }
    1146             : 
    1147             : namespace {
    1148             : 
    1149             : struct FindAnyFormula
    1150             : {
    1151           0 :     bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const
    1152             :     {
    1153           0 :         return true;
    1154             :     }
    1155             : };
    1156             : 
    1157             : }
    1158             : 
    1159           0 : bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
    1160             : {
    1161           0 :     if (nRow2 < nRow1 || !ValidRow(nRow1) || !ValidRow(nRow2))
    1162           0 :         return false;
    1163             : 
    1164             :     FindAnyFormula aFunc;
    1165             :     std::pair<sc::CellStoreType::const_iterator, size_t> aRet =
    1166           0 :         sc::FindFormula(maCells, nRow1, nRow2, aFunc);
    1167             : 
    1168           0 :     return aRet.first != maCells.end();
    1169             : }
    1170             : 
    1171             : namespace {
    1172             : 
    1173          79 : void endListening( sc::EndListeningContext& rCxt, ScFormulaCell** pp, ScFormulaCell** ppEnd )
    1174             : {
    1175         330 :     for (; pp != ppEnd; ++pp)
    1176             :     {
    1177         251 :         ScFormulaCell& rFC = **pp;
    1178         251 :         rFC.EndListeningTo(rCxt);
    1179             :     }
    1180          79 : }
    1181             : 
    1182             : class StartListeningFormulaCellsHandler
    1183             : {
    1184             :     sc::StartListeningContext& mrStartCxt;
    1185             :     sc::EndListeningContext& mrEndCxt;
    1186             :     SCROW mnStartRow;
    1187             :     SCROW mnEndRow;
    1188             : 
    1189             : public:
    1190       41778 :     StartListeningFormulaCellsHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
    1191       41778 :         mrStartCxt(rStartCxt), mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {}
    1192             : 
    1193       43018 :     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
    1194             :     {
    1195       43018 :         if (node.type != sc::element_type_formula)
    1196             :             // We are only interested in formulas.
    1197       85701 :             return;
    1198             : 
    1199         335 :         mnStartRow = node.position + nOffset;
    1200             : 
    1201         335 :         ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
    1202         335 :         ScFormulaCell** ppEnd = ppBeg + nDataSize;
    1203             : 
    1204         335 :         ScFormulaCell** pp = ppBeg;
    1205             : 
    1206             :         // If the first formula cell belongs to a group and it's not the top
    1207             :         // cell, move up to the top cell of the group, and have all the extra
    1208             :         // formula cells stop listening.
    1209             : 
    1210         335 :         ScFormulaCell* pFC = *pp;
    1211         335 :         if (pFC->IsShared() && !pFC->IsSharedTop())
    1212             :         {
    1213          78 :             SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
    1214          78 :             if (nBackTrackSize > 0)
    1215             :             {
    1216             :                 assert(static_cast<size_t>(nBackTrackSize) <= nOffset);
    1217         318 :                 for (SCROW i = 0; i < nBackTrackSize; ++i)
    1218         240 :                     --pp;
    1219          78 :                 endListening(mrEndCxt, pp, ppBeg);
    1220          78 :                 mnStartRow -= nBackTrackSize;
    1221             :             }
    1222             :         }
    1223             : 
    1224         717 :         for (; pp != ppEnd; ++pp)
    1225             :         {
    1226         382 :             pFC = *pp;
    1227             : 
    1228         382 :             if (!pFC->IsSharedTop())
    1229             :             {
    1230             :                 assert(!pFC->IsShared());
    1231         229 :                 pFC->StartListeningTo(mrStartCxt);
    1232         229 :                 continue;
    1233             :             }
    1234             : 
    1235             :             // If This is the last group in the range, see if the group
    1236             :             // extends beyond the range, in which case have the excess
    1237             :             // formula cells stop listening.
    1238         153 :             size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
    1239         153 :             mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
    1240         153 :             if (nEndGroupPos > nDataSize)
    1241             :             {
    1242           0 :                 size_t nExcessSize = nEndGroupPos - nDataSize;
    1243           0 :                 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
    1244           0 :                 ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
    1245           0 :                 endListening(mrEndCxt, ppGrp, ppGrpEnd);
    1246             : 
    1247             :                 // Register formula cells as a group.
    1248           0 :                 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
    1249           0 :                 pp = ppEnd - 1; // Move to the one before the end position.
    1250             :             }
    1251             :             else
    1252             :             {
    1253             :                 // Register formula cells as a group.
    1254         153 :                 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
    1255         153 :                 pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
    1256             :             }
    1257             :         }
    1258             :     }
    1259             : 
    1260           0 :     SCROW getStartRow() const
    1261             :     {
    1262           0 :         return mnStartRow;
    1263             :     }
    1264             : 
    1265           0 :     SCROW getEndRow() const
    1266             :     {
    1267           0 :         return mnEndRow;
    1268             :     }
    1269             : };
    1270             : 
    1271             : class EndListeningFormulaCellsHandler
    1272             : {
    1273             :     sc::EndListeningContext& mrEndCxt;
    1274             :     SCROW mnStartRow;
    1275             :     SCROW mnEndRow;
    1276             : 
    1277             : public:
    1278           1 :     EndListeningFormulaCellsHandler( sc::EndListeningContext& rEndCxt ) :
    1279           1 :         mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {}
    1280             : 
    1281           1 :     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
    1282             :     {
    1283           1 :         if (node.type != sc::element_type_formula)
    1284             :             // We are only interested in formulas.
    1285           1 :             return;
    1286             : 
    1287           1 :         mnStartRow = node.position + nOffset;
    1288             : 
    1289           1 :         ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
    1290           1 :         ScFormulaCell** ppEnd = ppBeg + nDataSize;
    1291             : 
    1292           1 :         ScFormulaCell** pp = ppBeg;
    1293             : 
    1294             :         // If the first formula cell belongs to a group and it's not the top
    1295             :         // cell, move up to the top cell of the group.
    1296             : 
    1297           1 :         ScFormulaCell* pFC = *pp;
    1298           1 :         if (pFC->IsShared() && !pFC->IsSharedTop())
    1299             :         {
    1300           0 :             SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
    1301           0 :             if (nBackTrackSize > 0)
    1302             :             {
    1303             :                 assert(static_cast<size_t>(nBackTrackSize) <= nOffset);
    1304           0 :                 for (SCROW i = 0; i < nBackTrackSize; ++i)
    1305           0 :                     --pp;
    1306           0 :                 mnStartRow -= nBackTrackSize;
    1307             :             }
    1308             :         }
    1309             : 
    1310           2 :         for (; pp != ppEnd; ++pp)
    1311             :         {
    1312           1 :             pFC = *pp;
    1313             : 
    1314           1 :             if (!pFC->IsSharedTop())
    1315             :             {
    1316             :                 assert(!pFC->IsShared());
    1317           0 :                 pFC->EndListeningTo(mrEndCxt);
    1318           0 :                 continue;
    1319             :             }
    1320             : 
    1321           1 :             size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
    1322           1 :             mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
    1323             : 
    1324           1 :             ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
    1325           1 :             endListening(mrEndCxt, pp, ppGrpEnd);
    1326             : 
    1327           1 :             if (nEndGroupPos > nDataSize)
    1328             :             {
    1329             :                 // The group goes beyond the specified end row.  Move to the
    1330             :                 // one before the end position to finish the loop.
    1331           1 :                 pp = ppEnd - 1;
    1332             :             }
    1333             :             else
    1334             :             {
    1335             :                 // Move to the last one in the group.
    1336           0 :                 pp += pFC->GetSharedLength() - 1;
    1337             :             }
    1338             :         }
    1339             :     }
    1340             : 
    1341           1 :     SCROW getStartRow() const
    1342             :     {
    1343           1 :         return mnStartRow;
    1344             :     }
    1345             : 
    1346           1 :     SCROW getEndRow() const
    1347             :     {
    1348           1 :         return mnEndRow;
    1349             :     }
    1350             : };
    1351             : 
    1352             : }
    1353             : 
    1354       41778 : void ScColumn::StartListeningFormulaCells(
    1355             :     sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
    1356             :     SCROW nRow1, SCROW nRow2, SCROW* pStartRow, SCROW* pEndRow )
    1357             : {
    1358       41778 :     StartListeningFormulaCellsHandler aFunc(rStartCxt, rEndCxt);
    1359       41778 :     sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    1360             : 
    1361       41778 :     if (pStartRow)
    1362             :         // start row position may be smaller than nRow1 in case the formula
    1363             :         // group starts before nRow1 position.
    1364           0 :         *pStartRow = aFunc.getStartRow();
    1365             : 
    1366       41778 :     if (pEndRow)
    1367             :         // row position of the last cell that started listening, which may be
    1368             :         // greater than nRow2 in case the formula group extends beyond nRow2.
    1369           0 :         *pEndRow = aFunc.getEndRow();
    1370       41778 : }
    1371             : 
    1372           1 : void ScColumn::EndListeningFormulaCells(
    1373             :     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
    1374             :     SCROW* pStartRow, SCROW* pEndRow )
    1375             : {
    1376           1 :     EndListeningFormulaCellsHandler aFunc(rCxt);
    1377           1 :     sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    1378             : 
    1379           1 :     if (pStartRow)
    1380           1 :         *pStartRow = aFunc.getStartRow();
    1381             : 
    1382           1 :     if (pEndRow)
    1383           1 :         *pEndRow = aFunc.getEndRow();
    1384           1 : }
    1385             : 
    1386          36 : void ScColumn::EndListeningIntersectedGroup(
    1387             :     sc::EndListeningContext& rCxt, SCROW nRow, std::vector<ScAddress>* pGroupPos )
    1388             : {
    1389          36 :     if (!ValidRow(nRow))
    1390           0 :         return;
    1391             : 
    1392          36 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
    1393          36 :     sc::CellStoreType::iterator it = aPos.first;
    1394          36 :     if (it->type != sc::element_type_formula)
    1395             :         // Only interested in a formula block.
    1396           0 :         return;
    1397             : 
    1398          36 :     ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
    1399          36 :     ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
    1400          36 :     if (!xGroup)
    1401             :         // Not a formula group.
    1402           0 :         return;
    1403             : 
    1404             :     // End listening.
    1405          36 :     pFC->EndListeningTo(rCxt);
    1406             : 
    1407          36 :     if (pGroupPos)
    1408             :     {
    1409          36 :         if (!pFC->IsSharedTop())
    1410             :             // Record the position of the top cell of the group.
    1411          20 :             pGroupPos->push_back(xGroup->mpTopCell->aPos);
    1412             : 
    1413          36 :         SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1;
    1414          36 :         if (nRow < nGrpLastRow)
    1415             :             // Record the last position of the group.
    1416          33 :             pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab));
    1417          36 :     }
    1418             : }
    1419             : 
    1420       72621 : void ScColumn::EndListeningIntersectedGroups(
    1421             :     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos )
    1422             : {
    1423             :     // Only end the intersected group.
    1424       72621 :     sc::CellStoreType::position_type aPos = maCells.position(nRow1);
    1425       72621 :     sc::CellStoreType::iterator it = aPos.first;
    1426       72621 :     if (it->type == sc::element_type_formula)
    1427             :     {
    1428          33 :         ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
    1429          33 :         ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
    1430          33 :         if (xGroup && !pFC->IsSharedTop())
    1431             :         {
    1432             :             // End listening.
    1433           7 :             pFC->EndListeningTo(rCxt);
    1434           7 :             if (pGroupPos)
    1435             :                 // Record the position of the top cell of the group.
    1436           7 :                 pGroupPos->push_back(xGroup->mpTopCell->aPos);
    1437          33 :         }
    1438             :     }
    1439             : 
    1440       72621 :     aPos = maCells.position(it, nRow2);
    1441       72621 :     it = aPos.first;
    1442       72621 :     if (it->type == sc::element_type_formula)
    1443             :     {
    1444          17 :         ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
    1445          17 :         ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
    1446          17 :         if (xGroup && !pFC->IsSharedTop())
    1447             :         {
    1448             :             // End listening.
    1449           6 :             pFC->EndListeningTo(rCxt);
    1450           6 :             if (pGroupPos)
    1451             :             {
    1452             :                 // Record the position of the bottom cell of the group.
    1453           6 :                 ScAddress aPosLast = xGroup->mpTopCell->aPos;
    1454           6 :                 aPosLast.IncRow(xGroup->mnLength-1);
    1455           6 :                 pGroupPos->push_back(aPosLast);
    1456             :             }
    1457          17 :         }
    1458             :     }
    1459       72621 : }
    1460             : 
    1461           6 : void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow )
    1462             : {
    1463           6 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
    1464           6 :     if (aPos.first->type != sc::element_type_formula)
    1465             :         // not a formula cell.
    1466           0 :         return;
    1467             : 
    1468           6 :     ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
    1469             : 
    1470           6 :     ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
    1471           6 :     if (!xGroup)
    1472             :     {
    1473             :         // not a formula group.
    1474           0 :         (*pp)->EndListeningTo(rCxt);
    1475           0 :         return;
    1476             :     }
    1477             : 
    1478             :     // Move back to the top cell.
    1479           6 :     SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
    1480             :     assert(nTopDelta >= 0);
    1481           6 :     if (nTopDelta > 0)
    1482           6 :         pp -= nTopDelta;
    1483             : 
    1484             :     // Set the needs listening flag to all cells in the group.
    1485             :     assert(*pp == xGroup->mpTopCell);
    1486           6 :     ScFormulaCell** ppEnd = pp + xGroup->mnLength;
    1487          34 :     for (; pp != ppEnd; ++pp)
    1488          34 :         (*pp)->EndListeningTo(rCxt);
    1489             : }
    1490             : 
    1491          72 : void ScColumn::SetNeedsListeningGroup( SCROW nRow )
    1492             : {
    1493          72 :     sc::CellStoreType::position_type aPos = maCells.position(nRow);
    1494          72 :     if (aPos.first->type != sc::element_type_formula)
    1495             :         // not a formula cell.
    1496          14 :         return;
    1497             : 
    1498          70 :     ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
    1499             : 
    1500          70 :     ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
    1501          70 :     if (!xGroup)
    1502             :     {
    1503             :         // not a formula group.
    1504          10 :         (*pp)->SetNeedsListening(true);
    1505          10 :         return;
    1506             :     }
    1507             : 
    1508             :     // Move back to the top cell.
    1509          60 :     SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
    1510             :     assert(nTopDelta >= 0);
    1511          60 :     if (nTopDelta > 0)
    1512          38 :         pp -= nTopDelta;
    1513             : 
    1514             :     // Set the needs listening flag to all cells in the group.
    1515             :     assert(*pp == xGroup->mpTopCell);
    1516          60 :     ScFormulaCell** ppEnd = pp + xGroup->mnLength;
    1517         462 :     for (; pp != ppEnd; ++pp)
    1518         462 :         (*pp)->SetNeedsListening(true);
    1519         156 : }
    1520             : 
    1521             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11