LCOV - code coverage report
Current view: top level - sc/source/core/tool - sharedformula.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 193 197 98.0 %
Date: 2015-06-13 12:38:46 Functions: 9 9 100.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 "sharedformula.hxx"
      11             : #include "calcmacros.hxx"
      12             : #include "tokenarray.hxx"
      13             : #include <listenercontext.hxx>
      14             : #include <document.hxx>
      15             : #include <grouparealistener.hxx>
      16             : 
      17             : namespace sc {
      18             : 
      19       57928 : void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type& aPos)
      20             : {
      21       57928 :     SCROW nRow = aPos.first->position + aPos.second;
      22             : 
      23       57928 :     if (aPos.first->type != sc::element_type_formula)
      24             :         // Not a formula cell block.
      25      115604 :         return;
      26             : 
      27         205 :     if (aPos.second == 0)
      28             :         // Split position coincides with the block border. Nothing to do.
      29         140 :         return;
      30             : 
      31          65 :     sc::formula_block::iterator it = sc::formula_block::begin(*aPos.first->data);
      32          65 :     std::advance(it, aPos.second);
      33          65 :     ScFormulaCell& rTop = **it;
      34          65 :     if (!rTop.IsShared())
      35             :         // Not a shared formula.
      36          13 :         return;
      37             : 
      38          52 :     if (nRow == rTop.GetSharedTopRow())
      39             :         // Already the top cell of a shared group.
      40           5 :         return;
      41             : 
      42          47 :     ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
      43             : 
      44          47 :     SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
      45          94 :     ScFormulaCellGroupRef xGroup2;
      46          47 :     if (nLength2 > 1)
      47             :     {
      48          37 :         xGroup2.reset(new ScFormulaCellGroup);
      49          37 :         xGroup2->mbInvariant = xGroup->mbInvariant;
      50          37 :         xGroup2->mpTopCell = &rTop;
      51          37 :         xGroup2->mnLength = nLength2;
      52          37 :         xGroup2->mpCode = xGroup->mpCode->Clone();
      53             :     }
      54             : 
      55          47 :     xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
      56          47 :     ScFormulaCell& rPrevTop = *sc::formula_block::at(*aPos.first->data, aPos.second - xGroup->mnLength);
      57             : 
      58             : #if USE_FORMULA_GROUP_LISTENER
      59             :     // At least group area listeners will have to be adapted. As long as
      60             :     // there's no update mechanism and no separated handling of group area and
      61             :     // other listeners, all listeners of this group's top cell are to be reset.
      62          47 :     if (nLength2)
      63             :     {
      64          47 :         rPrevTop.EndListeningTo( rPrevTop.GetDocument(), NULL, ScAddress( ScAddress::UNINITIALIZED));
      65          47 :         rPrevTop.SetNeedsListening(true);
      66             :     }
      67             : #endif
      68             : 
      69          47 :     if (xGroup->mnLength == 1)
      70             :     {
      71             :         // The top group consists of only one cell. Ungroup this.
      72          34 :         ScFormulaCellGroupRef xNone;
      73          34 :         rPrevTop.SetCellGroup(xNone);
      74             :     }
      75             : 
      76             :     // Apply the lower group object to the lower cells.
      77             : #if DEBUG_COLUMN_STORAGE
      78             :     if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > aPos.first->position + aPos.first->size)
      79             :     {
      80             :         cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
      81             :         cerr.flush();
      82             :         abort();
      83             :     }
      84             : #endif
      85          47 :     sc::formula_block::iterator itEnd = it;
      86          47 :     std::advance(itEnd, nLength2);
      87         197 :     for (; it != itEnd; ++it)
      88             :     {
      89         150 :         ScFormulaCell& rCell = **it;
      90         150 :         rCell.SetCellGroup(xGroup2);
      91          47 :     }
      92             : }
      93             : 
      94         611 : void SharedFormulaUtil::splitFormulaCellGroups(CellStoreType& rCells, std::vector<SCROW>& rBounds)
      95             : {
      96         611 :     if (rBounds.empty())
      97         368 :         return;
      98             : 
      99             :     // Sort and remove duplicates.
     100         427 :     std::sort(rBounds.begin(), rBounds.end());
     101         427 :     std::vector<SCROW>::iterator it = std::unique(rBounds.begin(), rBounds.end());
     102         427 :     rBounds.erase(it, rBounds.end());
     103             : 
     104         427 :     it = rBounds.begin();
     105         427 :     SCROW nRow = *it;
     106         427 :     CellStoreType::position_type aPos = rCells.position(nRow);
     107         427 :     if (aPos.first == rCells.end())
     108           0 :         return;
     109             : 
     110         427 :     splitFormulaCellGroup(aPos);
     111         427 :     std::vector<SCROW>::iterator itEnd = rBounds.end();
     112         734 :     for (++it; it != itEnd; ++it)
     113             :     {
     114         307 :         nRow = *it;
     115         307 :         aPos = rCells.position(aPos.first, nRow);
     116         307 :         if (aPos.first == rCells.end())
     117           0 :             return;
     118             : 
     119         307 :         splitFormulaCellGroup(aPos);
     120             :     }
     121             : }
     122             : 
     123        1075 : bool SharedFormulaUtil::joinFormulaCells(
     124             :     const CellStoreType::position_type& rPos, ScFormulaCell& rCell1, ScFormulaCell& rCell2 )
     125             : {
     126        1075 :     ScFormulaCell::CompareState eState = rCell1.CompareByTokenArray(rCell2);
     127        1075 :     if (eState == ScFormulaCell::NotEqual)
     128         380 :         return false;
     129             : 
     130             :     // Formula tokens equal those of the previous formula cell.
     131         695 :     ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
     132        1390 :     ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
     133         695 :     if (xGroup1)
     134             :     {
     135         543 :         if (xGroup2)
     136             :         {
     137             :             // Both cell 1 and cell 2 are shared. Merge them together.
     138          25 :             if (xGroup1.get() == xGroup2.get())
     139             :                 // They belong to the same group.
     140           6 :                 return false;
     141             : 
     142             :             // Set the group object from cell 1 to all cells in group 2.
     143          19 :             xGroup1->mnLength += xGroup2->mnLength;
     144          19 :             size_t nOffset = rPos.second + 1; // position of cell 2
     145          90 :             for (size_t i = 0, n = xGroup2->mnLength; i < n; ++i)
     146             :             {
     147          71 :                 ScFormulaCell& rCell = *sc::formula_block::at(*rPos.first->data, nOffset+i);
     148          71 :                 rCell.SetCellGroup(xGroup1);
     149             :             }
     150             :         }
     151             :         else
     152             :         {
     153             :             // cell 1 is shared but cell 2 is not.
     154         518 :             rCell2.SetCellGroup(xGroup1);
     155         518 :             ++xGroup1->mnLength;
     156             :         }
     157             :     }
     158             :     else
     159             :     {
     160         152 :         if (xGroup2)
     161             :         {
     162             :             // cell 1 is not shared, but cell 2 is already shared.
     163           5 :             rCell1.SetCellGroup(xGroup2);
     164           5 :             xGroup2->mpTopCell = &rCell1;
     165           5 :             ++xGroup2->mnLength;
     166             :         }
     167             :         else
     168             :         {
     169             :             // neither cells are shared.
     170             :             assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
     171         147 :             xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
     172         147 :             rCell2.SetCellGroup(xGroup1);
     173             :         }
     174             :     }
     175             : 
     176        1384 :     return true;
     177             : }
     178             : 
     179       47457 : bool SharedFormulaUtil::joinFormulaCellAbove( const CellStoreType::position_type& aPos )
     180             : {
     181       47457 :     if (aPos.first->type != sc::element_type_formula)
     182             :         // This is not a formula cell.
     183       47339 :         return false;
     184             : 
     185         118 :     if (aPos.second == 0)
     186             :         // This cell is already the top cell in a formula block; the previous
     187             :         // cell is not a formula cell.
     188         110 :         return false;
     189             : 
     190           8 :     ScFormulaCell& rPrev = *sc::formula_block::at(*aPos.first->data, aPos.second-1);
     191           8 :     ScFormulaCell& rCell = *sc::formula_block::at(*aPos.first->data, aPos.second);
     192           8 :     sc::CellStoreType::position_type aPosPrev = aPos;
     193           8 :     --aPosPrev.second;
     194           8 :     return joinFormulaCells(aPosPrev, rPrev, rCell);
     195             : }
     196             : 
     197         330 : void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& aPos, ScFormulaCell& rCell)
     198             : {
     199         330 :     if (!rCell.IsShared())
     200         559 :         return;
     201             : 
     202         101 :     ScFormulaCellGroupRef xNone;
     203         101 :     sc::CellStoreType::iterator it = aPos.first;
     204             : 
     205             :     // This formula cell is shared. Adjust the shared group.
     206         101 :     if (rCell.aPos.Row() == rCell.GetSharedTopRow())
     207             :     {
     208             :         // Top of the shared range.
     209          50 :         ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
     210          50 :         if (xGroup->mnLength == 2)
     211             :         {
     212             :             // Group consists only only two cells. Mark the second one non-shared.
     213             : #if DEBUG_COLUMN_STORAGE
     214             :             if (aPos.second+1 >= aPos.first->size)
     215             :             {
     216             :                 cerr << "ScColumn::UnshareFormulaCell: There is no next formula cell but there should be!" << endl;
     217             :                 cerr.flush();
     218             :                 abort();
     219             :             }
     220             : #endif
     221          14 :             ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
     222          14 :             rNext.SetCellGroup(xNone);
     223             :         }
     224             :         else
     225             :         {
     226             :             // Move the top cell to the next formula cell down.
     227          36 :             ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
     228          36 :             --xGroup->mnLength;
     229          36 :             xGroup->mpTopCell = &rNext;
     230          50 :         }
     231             :     }
     232          51 :     else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
     233             :     {
     234             :         // Bottom of the shared range.
     235          25 :         ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
     236          25 :         if (xGroup->mnLength == 2)
     237             :         {
     238             :             // Mark the top cell non-shared.
     239             : #if DEBUG_COLUMN_STORAGE
     240             :             if (aPos.second == 0)
     241             :             {
     242             :                 cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
     243             :                 cerr.flush();
     244             :                 abort();
     245             :             }
     246             : #endif
     247           9 :             ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
     248           9 :             rPrev.SetCellGroup(xNone);
     249             :         }
     250             :         else
     251             :         {
     252             :             // Just shortern the shared range length by one.
     253          16 :             --xGroup->mnLength;
     254          25 :         }
     255             :     }
     256             :     else
     257             :     {
     258             :         // In the middle of the shared range. Split it into two groups.
     259          26 :         ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
     260          26 :         SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
     261          26 :         xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
     262          26 :         if (xGroup->mnLength == 1)
     263             :         {
     264             :             // Make the top cell non-shared.
     265             : #if DEBUG_COLUMN_STORAGE
     266             :             if (aPos.second == 0)
     267             :             {
     268             :                 cerr << "ScColumn::UnshareFormulaCell: There is no previous formula cell but there should be!" << endl;
     269             :                 cerr.flush();
     270             :                 abort();
     271             :             }
     272             : #endif
     273           7 :             ScFormulaCell& rPrev = *sc::formula_block::at(*it->data, aPos.second-1);
     274           7 :             rPrev.SetCellGroup(xNone);
     275             :         }
     276             : 
     277          26 :         SCROW nLength2 = nEndRow - rCell.aPos.Row();
     278          26 :         if (nLength2 >= 2)
     279             :         {
     280          19 :             ScFormulaCellGroupRef xGroup2;
     281          19 :             xGroup2.reset(new ScFormulaCellGroup);
     282          19 :             ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
     283          19 :             xGroup2->mpTopCell = &rNext;
     284          19 :             xGroup2->mnLength = nLength2;
     285          19 :             xGroup2->mbInvariant = xGroup->mbInvariant;
     286          19 :             xGroup2->mpCode = xGroup->mpCode->Clone();
     287             : #if DEBUG_COLUMN_STORAGE
     288             :             if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > it->position + it->size)
     289             :             {
     290             :                 cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
     291             :                 cerr.flush();
     292             :                 abort();
     293             :             }
     294             : #endif
     295          19 :             sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
     296          19 :             std::advance(itCell, aPos.second+1);
     297          19 :             sc::formula_block::iterator itCellEnd = itCell;
     298          19 :             std::advance(itCellEnd, xGroup2->mnLength);
     299         115 :             for (; itCell != itCellEnd; ++itCell)
     300             :             {
     301          96 :                 ScFormulaCell& rCell2 = **itCell;
     302          96 :                 rCell2.SetCellGroup(xGroup2);
     303          19 :             }
     304             :         }
     305             :         else
     306             :         {
     307             :             // Make the next cell non-shared.
     308           7 :             sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
     309           7 :             std::advance(itCell, aPos.second+1);
     310           7 :             ScFormulaCell& rCell2 = **itCell;
     311           7 :             rCell2.SetCellGroup(xNone);
     312          26 :         }
     313             :     }
     314             : 
     315         101 :     rCell.SetCellGroup(xNone);
     316             : }
     317             : 
     318           5 : void SharedFormulaUtil::unshareFormulaCells(CellStoreType& rCells, std::vector<SCROW>& rRows)
     319             : {
     320           5 :     if (rRows.empty())
     321           5 :         return;
     322             : 
     323             :     // Sort and remove duplicates.
     324           5 :     std::sort(rRows.begin(), rRows.end());
     325           5 :     rRows.erase(std::unique(rRows.begin(), rRows.end()), rRows.end());
     326             : 
     327             :     // Add next cell positions to the list (to ensure that each position becomes a single cell).
     328           5 :     std::vector<SCROW> aRows2;
     329           5 :     std::vector<SCROW>::const_iterator it = rRows.begin(), itEnd = rRows.end();
     330          33 :     for (; it != itEnd; ++it)
     331             :     {
     332          28 :         if (*it > MAXROW)
     333           0 :             break;
     334             : 
     335          28 :         aRows2.push_back(*it);
     336             : 
     337          28 :         if (*it < MAXROW)
     338          28 :             aRows2.push_back(*it+1);
     339             :     }
     340             : 
     341             :     // Remove duplicates again (the vector should still be sorted).
     342           5 :     aRows2.erase(std::unique(aRows2.begin(), aRows2.end()), aRows2.end());
     343             : 
     344           5 :     splitFormulaCellGroups(rCells, aRows2);
     345             : }
     346             : 
     347        1044 : void SharedFormulaUtil::startListeningAsGroup( sc::StartListeningContext& rCxt, ScFormulaCell** ppSharedTop )
     348             : {
     349        1044 :     ScFormulaCell& rTopCell = **ppSharedTop;
     350             :     assert(rTopCell.IsSharedTop());
     351             : 
     352             : #if USE_FORMULA_GROUP_LISTENER
     353        1044 :     ScDocument& rDoc = rCxt.getDoc();
     354        1044 :     rDoc.SetDetectiveDirty(true);
     355             : 
     356        1044 :     ScFormulaCellGroupRef xGroup = rTopCell.GetCellGroup();
     357        1044 :     const ScTokenArray* pCode = xGroup->mpCode;
     358             :     assert(pCode == rTopCell.GetCode());
     359        1044 :     if (pCode->IsRecalcModeAlways())
     360             :     {
     361             :         rDoc.StartListeningArea(
     362             :             BCA_LISTEN_ALWAYS, false,
     363           1 :             xGroup->getAreaListener(ppSharedTop, BCA_LISTEN_ALWAYS, true, true));
     364             :     }
     365             : 
     366        1044 :     formula::FormulaToken** p = pCode->GetCode();
     367        1044 :     formula::FormulaToken** pEnd = p + pCode->GetCodeLen();
     368        6333 :     for (; p != pEnd; ++p)
     369             :     {
     370        5289 :         const formula::FormulaToken* t = *p;
     371        5289 :         switch (t->GetType())
     372             :         {
     373             :             case formula::svSingleRef:
     374             :             {
     375        1769 :                 const ScSingleRefData* pRef = t->GetSingleRef();
     376        1769 :                 ScAddress aPos = pRef->toAbs(rTopCell.aPos);
     377        1769 :                 ScFormulaCell** pp = ppSharedTop;
     378        1769 :                 ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
     379       11054 :                 for (; pp != ppEnd; ++pp)
     380             :                 {
     381        9285 :                     if (!aPos.IsValid())
     382           0 :                         break;
     383             : 
     384        9285 :                     rDoc.StartListeningCell(rCxt, aPos, **pp);
     385        9285 :                     if (pRef->IsRowRel())
     386        7797 :                         aPos.IncRow();
     387             :                 }
     388             :             }
     389        1769 :             break;
     390             :             case formula::svDoubleRef:
     391             :             {
     392         478 :                 const ScSingleRefData& rRef1 = *t->GetSingleRef();
     393         478 :                 const ScSingleRefData& rRef2 = *t->GetSingleRef2();
     394         478 :                 ScAddress aPos1 = rRef1.toAbs(rTopCell.aPos);
     395         478 :                 ScAddress aPos2 = rRef2.toAbs(rTopCell.aPos);
     396             : 
     397         478 :                 ScRange aOrigRange = ScRange(aPos1, aPos2);
     398         478 :                 ScRange aListenedRange = aOrigRange;
     399         478 :                 if (rRef2.IsRowRel())
     400         258 :                     aListenedRange.aEnd.IncRow(xGroup->mnLength-1);
     401             : 
     402         478 :                 if (aPos1.IsValid() && aPos2.IsValid())
     403             :                 {
     404             :                     rDoc.StartListeningArea(
     405             :                         aListenedRange, true,
     406         478 :                         xGroup->getAreaListener(ppSharedTop, aOrigRange, !rRef1.IsRowRel(), !rRef2.IsRowRel()));
     407             :                 }
     408             :             }
     409         478 :             break;
     410             :             default:
     411             :                 ;
     412             :         }
     413             :     }
     414             : 
     415        1044 :     ScFormulaCell** pp = ppSharedTop;
     416        1044 :     ScFormulaCell** ppEnd = ppSharedTop + xGroup->mnLength;
     417        6352 :     for (; pp != ppEnd; ++pp)
     418             :     {
     419        5308 :         ScFormulaCell& rCell = **pp;
     420        5308 :         rCell.SetNeedsListening(false);
     421        1044 :     }
     422             : 
     423             : #else
     424             :     ScFormulaCell** pp = ppSharedTop;
     425             :     ScFormulaCell** ppEnd = ppSharedTop + rTopCell.GetSharedLength();
     426             :     for (; pp != ppEnd; ++pp)
     427             :     {
     428             :         ScFormulaCell& rFC = **pp;
     429             :         rFC.StartListeningTo(rCxt);
     430             :     }
     431             : #endif
     432        1044 : }
     433             : 
     434         156 : }
     435             : 
     436             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11