LCOV - code coverage report
Current view: top level - sc/source/core/tool - grouparealistener.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 97 134 72.4 %
Date: 2015-06-13 12:38:46 Functions: 19 24 79.2 %
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 <grouparealistener.hxx>
      11             : #include <brdcst.hxx>
      12             : #include <formulacell.hxx>
      13             : #include <bulkdatahint.hxx>
      14             : #include <columnspanset.hxx>
      15             : #include <column.hxx>
      16             : #include <listenerquery.hxx>
      17             : #include <listenerqueryids.hxx>
      18             : #include <document.hxx>
      19             : #include <table.hxx>
      20             : 
      21             : namespace sc {
      22             : 
      23             : namespace {
      24             : 
      25             : class Notifier : std::unary_function<ScFormulaCell*, void>
      26             : {
      27             :     const SfxHint& mrHint;
      28             : public:
      29          12 :     Notifier( const SfxHint& rHint ) : mrHint(rHint) {}
      30             : 
      31          34 :     void operator() ( ScFormulaCell* pCell )
      32             :     {
      33          34 :         pCell->Notify(mrHint);
      34          34 :     }
      35             : };
      36             : 
      37          12 : class CollectCellAction : public sc::ColumnSpanSet::ColumnAction
      38             : {
      39             :     const FormulaGroupAreaListener& mrAreaListener;
      40             :     ScAddress maPos;
      41             :     std::vector<ScFormulaCell*> maCells;
      42             : 
      43             : public:
      44          12 :     CollectCellAction( const FormulaGroupAreaListener& rAreaListener ) :
      45          12 :         mrAreaListener(rAreaListener) {}
      46             : 
      47          17 :     virtual void startColumn( ScColumn* pCol ) SAL_OVERRIDE
      48             :     {
      49          17 :         maPos.SetTab(pCol->GetTab());
      50          17 :         maPos.SetCol(pCol->GetCol());
      51          17 :     }
      52             : 
      53          48 :     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) SAL_OVERRIDE
      54             :     {
      55          48 :         if (!bVal)
      56          79 :             return;
      57             : 
      58          17 :         mrAreaListener.collectFormulaCells(maPos.Tab(), maPos.Col(), nRow1, nRow2, maCells);
      59             :     };
      60             : 
      61          12 :     void swapCells( std::vector<ScFormulaCell*>& rCells )
      62             :     {
      63             :         // Remove duplicate before the swap.
      64          12 :         std::sort(maCells.begin(), maCells.end());
      65          12 :         std::vector<ScFormulaCell*>::iterator it = std::unique(maCells.begin(), maCells.end());
      66          12 :         maCells.erase(it, maCells.end());
      67             : 
      68          12 :         rCells.swap(maCells);
      69          12 :     }
      70             : };
      71             : 
      72             : }
      73             : 
      74         478 : FormulaGroupAreaListener::FormulaGroupAreaListener( const ScRange& rRange, const ScDocument& rDocument,
      75             :         const ScAddress& rTopCellPos, SCROW nGroupLen, bool bStartFixed, bool bEndFixed ) :
      76             :     maRange(rRange),
      77             :     mpColumn(NULL),
      78         478 :     mnTopCellRow(rTopCellPos.Row()),
      79             :     mnGroupLen(nGroupLen),
      80             :     mbStartFixed(bStartFixed),
      81         956 :     mbEndFixed(bEndFixed)
      82             : {
      83         478 :     const ScTable* pTab = rDocument.FetchTable( rTopCellPos.Tab());
      84             :     assert(pTab);
      85         478 :     mpColumn = pTab->FetchColumn( rTopCellPos.Col());
      86             :     assert(mpColumn);
      87             :     SAL_INFO( "sc.core.grouparealistener",
      88             :             "FormulaGroupAreaListener ctor this " << this <<
      89             :             " range " << (maRange == BCA_LISTEN_ALWAYS ? "LISTEN-ALWAYS" : maRange.Format(SCA_VALID)) <<
      90             :             " mnTopCellRow " << mnTopCellRow << " length " << mnGroupLen <<
      91             :             ", col/tab " << mpColumn->GetCol() << "/" << mpColumn->GetTab());
      92         478 : }
      93             : 
      94         956 : FormulaGroupAreaListener::~FormulaGroupAreaListener()
      95             : {
      96             :     SAL_INFO( "sc.core.grouparealistener",
      97             :             "FormulaGroupAreaListener dtor this " << this <<
      98             :             " range " << (maRange == BCA_LISTEN_ALWAYS ? "LISTEN-ALWAYS" : maRange.Format(SCA_VALID)) <<
      99             :             " mnTopCellRow " << mnTopCellRow << " length " << mnGroupLen <<
     100             :             ", col/tab " << mpColumn->GetCol() << "/" << mpColumn->GetTab());
     101         956 : }
     102             : 
     103          13 : ScRange FormulaGroupAreaListener::getListeningRange() const
     104             : {
     105          13 :     ScRange aRet = maRange;
     106          13 :     if (!mbEndFixed)
     107          12 :         aRet.aEnd.IncRow(mnGroupLen-1);
     108          13 :     return aRet;
     109             : }
     110             : 
     111         476 : void FormulaGroupAreaListener::Notify( const SfxHint& rHint )
     112             : {
     113         476 :     const SfxSimpleHint* pSimpleHint = dynamic_cast<const SfxSimpleHint*>(&rHint);
     114         476 :     if (!pSimpleHint)
     115         480 :         return;
     116             : 
     117         472 :     switch (pSimpleHint->GetId())
     118             :     {
     119             :         case SC_HINT_DATACHANGED:
     120           0 :             notifyCellChange(rHint, static_cast<const ScHint*>(pSimpleHint)->GetAddress());
     121           0 :         break;
     122             :         case SC_HINT_BULK_DATACHANGED:
     123             :         {
     124          12 :             const BulkDataHint& rBulkHint = static_cast<const BulkDataHint&>(*pSimpleHint);
     125          12 :             notifyBulkChange(rBulkHint);
     126             :         }
     127          12 :         break;
     128             :         default:
     129             :             ;
     130             :     }
     131             : }
     132             : 
     133           4 : void FormulaGroupAreaListener::Query( QueryBase& rQuery ) const
     134             : {
     135           4 :     switch (rQuery.getId())
     136             :     {
     137             :         case SC_LISTENER_QUERY_FORMULA_GROUP_RANGE:
     138             :         {
     139           4 :             const ScFormulaCell* pTop = getTopCell();
     140           4 :             ScRange aRange(pTop->aPos);
     141           4 :             aRange.aEnd.IncRow(mnGroupLen-1);
     142           4 :             QueryRange& rQR = static_cast<QueryRange&>(rQuery);
     143           4 :             rQR.add(aRange);
     144             :         }
     145           4 :         break;
     146             :         default:
     147             :             ;
     148             :     }
     149           4 : }
     150             : 
     151          12 : void FormulaGroupAreaListener::notifyBulkChange( const BulkDataHint& rHint )
     152             : {
     153          12 :     const ColumnSpanSet* pSpans = rHint.getSpans();
     154          12 :     if (!pSpans)
     155          12 :         return;
     156             : 
     157          12 :     ScDocument& rDoc = const_cast<BulkDataHint&>(rHint).getDoc();
     158             : 
     159          12 :     CollectCellAction aAction(*this);
     160          12 :     pSpans->executeColumnAction(rDoc, aAction);
     161             : 
     162          24 :     std::vector<ScFormulaCell*> aCells;
     163          12 :     aAction.swapCells(aCells);
     164          24 :     ScHint aHint(SC_HINT_DATACHANGED, ScAddress());
     165          24 :     std::for_each(aCells.begin(), aCells.end(), Notifier(aHint));
     166             : }
     167             : 
     168          17 : void FormulaGroupAreaListener::collectFormulaCells(
     169             :     SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const
     170             : {
     171          17 :     PutInOrder(nRow1, nRow2);
     172             : 
     173          17 :     if (nTab < maRange.aStart.Tab() || maRange.aEnd.Tab() < nTab)
     174             :         // Wrong sheet.
     175           0 :         return;
     176             : 
     177          17 :     if (nCol < maRange.aStart.Col() || maRange.aEnd.Col() < nCol)
     178             :         // Outside the column range.
     179           0 :         return;
     180             : 
     181          17 :     collectFormulaCells(nRow1, nRow2, rCells);
     182             : }
     183             : 
     184          17 : void FormulaGroupAreaListener::collectFormulaCells(
     185             :     SCROW nRow1, SCROW nRow2, std::vector<ScFormulaCell*>& rCells ) const
     186             : {
     187             :     SAL_INFO( "sc.core.grouparealistener",
     188             :             "FormulaGroupAreaListener::collectFormulaCells() this " << this <<
     189             :             " range " << (maRange == BCA_LISTEN_ALWAYS ? "LISTEN-ALWAYS" : maRange.Format(SCA_VALID)) <<
     190             :             " mnTopCellRow " << mnTopCellRow << " length " << mnGroupLen <<
     191             :             ", col/tab " << mpColumn->GetCol() << "/" << mpColumn->GetTab());
     192             : 
     193          17 :     size_t nBlockSize = 0;
     194          17 :     ScFormulaCell* const * pp = mpColumn->GetFormulaCellBlockAddress( mnTopCellRow, nBlockSize);
     195          17 :     if (!pp)
     196             :     {
     197             :         SAL_WARN("sc", "GetFormulaCellBlockAddress not found");
     198           0 :         return;
     199             :     }
     200             : 
     201             :     /* FIXME: with tdf#89957 it happened that the actual block size in column
     202             :      * AP (shifted from AO) of sheet 'w' was smaller than the remembered group
     203             :      * length and correct. This is just a very ugly workaround, the real cause
     204             :      * is yet unknown, but at least don't crash in such case. The intermediate
     205             :      * cause is that not all affected group area listeners are destroyed and
     206             :      * newly created, so mpColumn still points to the old column that then has
     207             :      * the content of a shifted column. Effectively this workaround has the
     208             :      * consequence that the group area listener is fouled up and not all
     209             :      * formula cells are notified.. */
     210          17 :     if (nBlockSize < static_cast<size_t>(mnGroupLen))
     211             :     {
     212             :         SAL_WARN("sc.core","FormulaGroupAreaListener::collectFormulaCells() nBlockSize " <<
     213             :                 nBlockSize << " < " << mnGroupLen << " mnGroupLen");
     214           0 :         const_cast<FormulaGroupAreaListener*>(this)->mnGroupLen = static_cast<SCROW>(nBlockSize);
     215             :     }
     216             : 
     217          17 :     ScFormulaCell* const * ppEnd = pp + mnGroupLen;
     218             : 
     219          17 :     if (mbStartFixed)
     220             :     {
     221           1 :         if (mbEndFixed)
     222             :         {
     223             :             // Both top and bottom row positions are absolute.  Use the original range as-is.
     224           1 :             SCROW nRefRow1 = maRange.aStart.Row();
     225           1 :             SCROW nRefRow2 = maRange.aEnd.Row();
     226           1 :             if (nRow2 < nRefRow1 || nRefRow2 < nRow1)
     227           0 :                 return;
     228             : 
     229           3 :             for (; pp != ppEnd; ++pp)
     230           2 :                 rCells.push_back(*pp);
     231             :         }
     232             :         else
     233             :         {
     234             :             // Only the end row is relative.
     235           0 :             SCROW nRefRow1 = maRange.aStart.Row();
     236           0 :             SCROW nRefRow2 = maRange.aEnd.Row();
     237           0 :             SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1;
     238           0 :             if (nRow2 < nRefRow1 || nMaxRefRow < nRow1)
     239           0 :                 return;
     240             : 
     241           0 :             if (nRefRow2 < nRow1)
     242             :             {
     243             :                 // Skip ahead to the first hit.
     244           0 :                 SCROW nSkip = nRow1 - nRefRow2;
     245           0 :                 pp += nSkip;
     246           0 :                 nRefRow2 += nSkip;
     247             :             }
     248             : 
     249             :             assert(nRow1 <= nRefRow2);
     250             : 
     251             :             // Notify the first hit cell and all subsequent ones.
     252           0 :             for (; pp != ppEnd; ++pp)
     253           0 :                 rCells.push_back(*pp);
     254             :         }
     255             :     }
     256          16 :     else if (mbEndFixed)
     257             :     {
     258             :         // Only the start row is relative.
     259           0 :         SCROW nRefRow1 = maRange.aStart.Row();
     260           0 :         SCROW nRefRow2 = maRange.aEnd.Row();
     261             : 
     262           0 :         if (nRow2 < nRefRow1 || nRefRow2 < nRow1)
     263           0 :             return;
     264             : 
     265           0 :         for (; pp != ppEnd && nRefRow1 <= nRefRow2; ++pp, ++nRefRow1)
     266           0 :             rCells.push_back(*pp);
     267             :     }
     268             :     else
     269             :     {
     270             :         // Both top and bottom row positions are relative.
     271          16 :         SCROW nRefRow1 = maRange.aStart.Row();
     272          16 :         SCROW nRefRow2 = maRange.aEnd.Row();
     273          16 :         SCROW nMaxRefRow = nRefRow2 + mnGroupLen - 1;
     274          16 :         if (nMaxRefRow < nRow1)
     275           0 :             return;
     276             : 
     277          16 :         if (nRefRow2 < nRow1)
     278             :         {
     279             :             // The ref row range is above the changed row span.  Skip ahead.
     280           4 :             SCROW nSkip = nRow1 - nRefRow2;
     281           4 :             pp += nSkip;
     282           4 :             nRefRow1 += nSkip;
     283           4 :             nRefRow2 += nSkip;
     284             :         }
     285             : 
     286             :         // At this point the initial ref row range should be overlapping the
     287             :         // dirty cell range.
     288             :         assert(nRow1 <= nRefRow2);
     289             : 
     290             :         // Keep sliding down until the top ref row position is below the
     291             :         // bottom row of the dirty cell range.
     292          70 :         for (; pp != ppEnd && nRefRow1 <= nRow2; ++pp, ++nRefRow1, ++nRefRow2)
     293          54 :             rCells.push_back(*pp);
     294             :     }
     295             : }
     296             : 
     297           0 : ScAddress FormulaGroupAreaListener::getTopCellPos() const
     298             : {
     299           0 :     const ScFormulaCell* p = getTopCell();
     300           0 :     return p ? p->aPos : ScAddress();
     301             : }
     302             : 
     303           4 : const ScFormulaCell* FormulaGroupAreaListener::getTopCell() const
     304             : {
     305           4 :     size_t nBlockSize = 0;
     306           4 :     const ScFormulaCell* const * pp = mpColumn->GetFormulaCellBlockAddress( mnTopCellRow, nBlockSize);
     307             :     SAL_WARN_IF(!pp, "sc", "GetFormulaCellBlockAddress not found");
     308           4 :     return pp ? *pp : NULL;
     309             : }
     310             : 
     311           0 : const ScRange& FormulaGroupAreaListener::getRange() const
     312             : {
     313           0 :     return maRange;
     314             : }
     315             : 
     316           0 : SCROW FormulaGroupAreaListener::getGroupLength() const
     317             : {
     318           0 :     return mnGroupLen;
     319             : }
     320             : 
     321           0 : void FormulaGroupAreaListener::notifyCellChange( const SfxHint& rHint, const ScAddress& rPos )
     322             : {
     323             :     // Determine which formula cells within the group need to be notified of this change.
     324           0 :     std::vector<ScFormulaCell*> aCells;
     325           0 :     collectFormulaCells(rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Row(), aCells);
     326           0 :     std::for_each(aCells.begin(), aCells.end(), Notifier(rHint));
     327           0 : }
     328             : 
     329         156 : }
     330             : 
     331             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11