Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  documen7.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <document.hxx>
#include <brdcst.hxx>
#include <bcaslot.hxx>
#include <formulacell.hxx>
#include <table.hxx>
#include <progress.hxx>
#include <scmod.hxx>
#include <inputopt.hxx>
#include <sheetevents.hxx>
#include <tokenarray.hxx>
#include <listenercontext.hxx>

void ScDocument::StartListeningArea(
    const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
{
    if (!pBASM)
        return;

    // Ensure sane ranges for the slots, specifically don't attempt to listen
    // to more sheets than the document has. The slot machine handles it but
    // with memory waste. Binary import filters can set out-of-bounds ranges
    // in formula expressions' references, so all middle layers would have to
    // check it, rather have this central point here.
    ScRange aLimitedRange( ScAddress::UNINITIALIZED );
    bool bEntirelyOut;
    if (!LimitRangeToAvailableSheets( rRange, aLimitedRange, bEntirelyOut))
    {
        pBASM->StartListeningArea(rRange, bGroupListening, pListener);
        return;
    }

    // If both sheets are out-of-bounds in the same direction then just bail out.
    if (bEntirelyOut)
        return;

    pBASM->StartListeningArea( aLimitedRange, bGroupListening, pListener);
}

void ScDocument::EndListeningArea( const ScRange& rRange, bool bGroupListening, SvtListener* pListener )
{
    if (!pBASM)
        return;

    // End listening has to limit the range exactly the same as in
    // StartListeningArea(), otherwise the range would not be found.
    ScRange aLimitedRange( ScAddress::UNINITIALIZED );
    bool bEntirelyOut;
    if (!LimitRangeToAvailableSheets( rRange, aLimitedRange, bEntirelyOut))
    {
        pBASM->EndListeningArea(rRange, bGroupListening, pListener);
        return;
    }

    // If both sheets are out-of-bounds in the same direction then just bail out.
    if (bEntirelyOut)
        return;

    pBASM->EndListeningArea( aLimitedRange, bGroupListening, pListener);
}

bool ScDocument::LimitRangeToAvailableSheets( const ScRange& rRange, ScRange& o_rRange,
        bool& o_bEntirelyOutOfBounds ) const
{
    const SCTAB nMaxTab = GetTableCount() - 1;
    if (ValidTab( rRange.aStart.Tab(), nMaxTab) && ValidTab( rRange.aEnd.Tab(), nMaxTab))
        return false;

    // Originally BCA_LISTEN_ALWAYS uses an implicit tab 0 and should had been
    // valid already, but in case that would change...
    if (rRange == BCA_LISTEN_ALWAYS)
        return false;

    SCTAB nTab1 = rRange.aStart.Tab();
    SCTAB nTab2 = rRange.aEnd.Tab();
    SAL_WARN("sc.core","ScDocument::LimitRangeToAvailableSheets - bad sheet range: " << nTab1 << ".." << nTab2 <<
            ", sheets: 0.." << nMaxTab);

    // Both sheets are out-of-bounds in the same direction.
    if ((nTab1 < 0 && nTab2 < 0) || (nMaxTab < nTab1 && nMaxTab < nTab2))
    {
        o_bEntirelyOutOfBounds = true;
        return true;
    }

    // Limit the sheet range to bounds.
    o_bEntirelyOutOfBounds = false;
    nTab1 = std::clamp<SCTAB>( nTab1, 0, nMaxTab);
    nTab2 = std::clamp<SCTAB>( nTab2, 0, nMaxTab);
    o_rRange = rRange;
    o_rRange.aStart.SetTab(nTab1);
    o_rRange.aEnd.SetTab(nTab2);
    return true;
}

void ScDocument::Broadcast( const ScHint& rHint )
{
    if ( !pBASM )
        return ;    // Clipboard or Undo
    if ( eHardRecalcState == HardRecalcState::OFF )
    {
        ScBulkBroadcast aBulkBroadcast( pBASM.get(), rHint.GetId());     // scoped bulk broadcast
        bool bIsBroadcasted = BroadcastHintInternal(rHint);
        if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted )
            TrackFormulas( rHint.GetId() );
    }

    if ( rHint.GetStartAddress() != BCA_BRDCST_ALWAYS )
    {
        SCTAB nTab = rHint.GetStartAddress().Tab();
        if (nTab < GetTableCount() && maTabs[nTab])
            maTabs[nTab]->SetStreamValid(false);
    }
}

bool ScDocument::BroadcastHintInternal( const ScHint& rHint )
{
    bool bIsBroadcasted = false;
    const ScAddress& address(rHint.GetStartAddress());
    SvtBroadcaster* pLastBC = nullptr;
    // Process all broadcasters for the given row range.
    for( SCROW nRow = 0; nRow < rHint.GetRowCount(); ++nRow )
    {
        ScAddress a(address);
        a.SetRow(address.Row() + nRow);
        SvtBroadcaster* pBC = GetBroadcaster(a);
        if ( pBC && pBC != pLastBC )
        {
            pBC->Broadcast( rHint );
            bIsBroadcasted = true;
            pLastBC = pBC;
        }
    }
    return bIsBroadcasted;
}

void ScDocument::BroadcastCells( const ScRange& rRange, SfxHintId nHint, bool bBroadcastSingleBroadcasters )
{
    PrepareFormulaCalc();

    if (!pBASM)
        return;    // Clipboard or Undo

    SCTAB nTab1 = rRange.aStart.Tab();
    SCTAB nTab2 = rRange.aEnd.Tab();
    SCROW nRow1 = rRange.aStart.Row();
    SCROW nRow2 = rRange.aEnd.Row();
    SCCOL nCol1 = rRange.aStart.Col();
    SCCOL nCol2 = rRange.aEnd.Col();

    if (eHardRecalcState == HardRecalcState::OFF)
    {
        ScBulkBroadcast aBulkBroadcast( pBASM.get(), nHint);     // scoped bulk broadcast
        bool bIsBroadcasted = false;

        if (bBroadcastSingleBroadcasters)
        {
            for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
            {
                ScTable* pTab = FetchTable(nTab);
                if (!pTab)
                    continue;

                bIsBroadcasted |= pTab->BroadcastBroadcasters( nCol1, nRow1, nCol2, nRow2, nHint);
            }
        }

        if (pBASM->AreaBroadcast(rRange, nHint) || bIsBroadcasted)
            TrackFormulas(nHint);
    }

    for (SCTAB nTab = nTab1; nTab <= nTab2; ++nTab)
    {
        ScTable* pTab = FetchTable(nTab);
        if (pTab)
            pTab->SetStreamValid(false);
    }

    BroadcastUno(SfxHint(SfxHintId::ScDataChanged));
}

void ScDocument::AreaBroadcast( const ScHint& rHint )
{
    if ( !pBASM )
        return ;    // Clipboard or Undo
    if (eHardRecalcState == HardRecalcState::OFF)
    {
        ScBulkBroadcast aBulkBroadcast( pBASM.get(), rHint.GetId());     // scoped bulk broadcast
        if ( pBASM->AreaBroadcast( rHint ) )
            TrackFormulas( rHint.GetId() );
    }
}

void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange )
{
    if ( pBASM )
        pBASM->DelBroadcastAreasInRange( rRange );
}

void ScDocument::StartListeningCell( const ScAddress& rAddress,
                                            SvtListener* pListener )
{
    OSL_ENSURE(pListener, "StartListeningCell: pListener Null");
    SCTAB nTab = rAddress.Tab();
    if (ScTable* pTable = FetchTable(nTab))
        pTable->StartListening(rAddress, pListener);
}

void ScDocument::EndListeningCell( const ScAddress& rAddress,
                                            SvtListener* pListener )
{
    OSL_ENSURE(pListener, "EndListeningCell: pListener Null");
    SCTAB nTab = rAddress.Tab();
    if (ScTable* pTable = FetchTable(nTab))
        pTable->EndListening( rAddress, pListener );
}

void ScDocument::StartListeningCell(
    sc::StartListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
{
    if (ScTable* pTable = FetchTable(rPos.Tab()))
        pTable->StartListening(rCxt, rPos, rListener);
}

void ScDocument::EndListeningCell(
    sc::EndListeningContext& rCxt, const ScAddress& rPos, SvtListener& rListener )
{
    if (ScTable* pTable = FetchTable(rPos.Tab()))
        pTable->EndListening(rCxt, rPos, rListener);
}

void ScDocument::EndListeningFormulaCells( std::vector<ScFormulaCell*>& rCells )
{
    if (rCells.empty())
        return;

    sc::EndListeningContext aCxt(*this);
    for (auto& pCell : rCells)
        pCell->EndListeningTo(aCxt);

    aCxt.purgeEmptyBroadcasters();
}

void ScDocument::PutInFormulaTree( ScFormulaCell* pCell )
{
    OSL_ENSURE( pCell, "PutInFormulaTree: pCell Null" );
    RemoveFromFormulaTree( pCell );
    // append
    ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
    if ( pEOFormulaTree )
        pEOFormulaTree->SetNext( pCell );
    else
        pFormulaTree = pCell;               // No end, no beginning...
    pCell->SetPrevious( pEOFormulaTree );
    pCell->SetNext( nullptr );
    pEOFormulaTree = pCell;
    nFormulaCodeInTree += pCell->GetCode()->GetCodeLen();
}

void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell )
{
    ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
    assert(pCell && "RemoveFromFormulaTree: pCell Null");
    ScFormulaCell* pPrev = pCell->GetPrevious();
    assert(pPrev != pCell);                 // pointing to itself?!?
    // if the cell is first or somewhere in chain
    if ( pPrev || pFormulaTree == pCell )
    {
        ScFormulaCell* pNext = pCell->GetNext();
        assert(pNext != pCell);             // pointing to itself?!?
        if ( pPrev )
        {
            assert(pFormulaTree != pCell);  // if this cell is also head something's wrong
            pPrev->SetNext( pNext );        // predecessor exists, set successor
        }
        else
        {
            pFormulaTree = pNext;           // this cell was first cell
        }
        if ( pNext )
        {
            assert(pEOFormulaTree != pCell); // if this cell is also tail something's wrong
            pNext->SetPrevious( pPrev );    // successor exists, set predecessor
        }
        else
        {
            pEOFormulaTree = pPrev;         // this cell was last cell
        }
        pCell->SetPrevious( nullptr );
        pCell->SetNext( nullptr );
        sal_uInt16 nRPN = pCell->GetCode()->GetCodeLen();
        if ( nFormulaCodeInTree >= nRPN )
            nFormulaCodeInTree -= nRPN;
        else
        {
            OSL_FAIL( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" );
            nFormulaCodeInTree = 0;
        }
    }
    else if ( !pFormulaTree && nFormulaCodeInTree )
    {
        OSL_FAIL( "!pFormulaTree && nFormulaCodeInTree != 0" );
        nFormulaCodeInTree = 0;
    }
}

void ScDocument::CalcFormulaTree( bool bOnlyForced, bool bProgressBar, bool bSetAllDirty )
{
    OSL_ENSURE( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" );
    // never ever recurse into this, might end up lost in infinity
    if ( IsCalculatingFormulaTree() )
        return ;

    ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
    mpFormulaGroupCxt.reset();
    bCalculatingFormulaTree = true;

    SetForcedFormulaPending( false );
    bool bOldIdleEnabled = IsIdleEnabled();
    EnableIdle(false);
    bool bOldAutoCalc = GetAutoCalc();
    //ATTENTION: _not_ SetAutoCalc( true ) because this might call CalcFormulaTree( true )
    //ATTENTION: if it was disabled before and bHasForcedFormulas is set
    bAutoCalc = true;
    if (eHardRecalcState == HardRecalcState::ETERNAL)
        CalcAll();
    else
    {
        ::std::vector<ScFormulaCell*> vAlwaysDirty;
        ScFormulaCell* pCell = pFormulaTree;
        while ( pCell )
        {
            if ( pCell->GetDirty() )
                ;   // nothing to do
            else if ( pCell->GetCode()->IsRecalcModeAlways() )
            {
                // pCell and dependents are to be set dirty again, collect
                // them first and broadcast afterwards to not break the
                // FormulaTree chain here.
                vAlwaysDirty.push_back( pCell);
            }
            else if ( bSetAllDirty )
            {
                // Force calculating all in tree, without broadcasting.
                pCell->SetDirtyVar();
            }
            pCell = pCell->GetNext();
        }
        for (const auto& rpCell : vAlwaysDirty)
        {
            pCell = rpCell;
            if (!pCell->GetDirty())
                pCell->SetDirty();
        }

        bool bProgress = !bOnlyForced && nFormulaCodeInTree && bProgressBar;
        if ( bProgress )
            ScProgress::CreateInterpretProgress( this );

        pCell = pFormulaTree;
        ScFormulaCell* pLastNoGood = nullptr;
        while ( pCell )
        {
            // Interpret resets bDirty and calls Remove, also the referenced!
            // the Cell remains when ScRecalcMode::ALWAYS.
            if ( bOnlyForced )
            {
                if ( pCell->GetCode()->IsRecalcModeForced() )
                    pCell->Interpret();
            }
            else
            {
                pCell->Interpret();
            }
            if ( pCell->GetPrevious() || pCell == pFormulaTree )
            {   // (IsInFormulaTree(pCell)) no Remove was called => next
                pLastNoGood = pCell;
                pCell = pCell->GetNext();
            }
            else
            {
                if ( pFormulaTree )
                {
                    if ( pFormulaTree->GetDirty() && !bOnlyForced )
                    {
                        pCell = pFormulaTree;
                        pLastNoGood = nullptr;
                    }
                    else
                    {
                        // IsInFormulaTree(pLastNoGood)
                        if ( pLastNoGood && (pLastNoGood->GetPrevious() ||
                                pLastNoGood == pFormulaTree) )
                            pCell = pLastNoGood->GetNext();
                        else
                        {
                            pCell = pFormulaTree;
                            while ( pCell && !pCell->GetDirty() )
                                pCell = pCell->GetNext();
                            if ( pCell )
                                pLastNoGood = pCell->GetPrevious();
                        }
                    }
                }
                else
                    pCell = nullptr;
            }
        }
        if ( bProgress )
            ScProgress::DeleteInterpretProgress();
    }
    bAutoCalc = bOldAutoCalc;
    EnableIdle(bOldIdleEnabled);
    bCalculatingFormulaTree = false;

    mpFormulaGroupCxt.reset();
}

void ScDocument::ClearFormulaTree()
{
    ScFormulaCell* pCell;
    ScFormulaCell* pTree = pFormulaTree;
    while ( pTree )
    {
        pCell = pTree;
        pTree = pCell->GetNext();
        if ( !pCell->GetCode()->IsRecalcModeAlways() )
            RemoveFromFormulaTree( pCell );
    }
}

void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell )
{
    OSL_ENSURE( pCell, "AppendToFormulaTrack: pCell Null" );
    // The cell can not be in both lists at the same time
    RemoveFromFormulaTrack( pCell );
    RemoveFromFormulaTree( pCell );
    if ( pEOFormulaTrack )
        pEOFormulaTrack->SetNextTrack( pCell );
    else
        pFormulaTrack = pCell;              // No end, no beginning...
    pCell->SetPreviousTrack( pEOFormulaTrack );
    pCell->SetNextTrack( nullptr );
    pEOFormulaTrack = pCell;
    ++nFormulaTrackCount;
}

void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell )
{
    assert(pCell && "RemoveFromFormulaTrack: pCell Null");
    ScFormulaCell* pPrev = pCell->GetPreviousTrack();
    assert(pPrev != pCell);                     // pointing to itself?!?
    // if the cell is first or somewhere in chain
    if ( !(pPrev || pFormulaTrack == pCell) )
        return;

    ScFormulaCell* pNext = pCell->GetNextTrack();
    assert(pNext != pCell);                 // pointing to itself?!?
    if ( pPrev )
    {
        assert(pFormulaTrack != pCell);     // if this cell is also head something's wrong
        pPrev->SetNextTrack( pNext );       // predecessor exists, set successor
    }
    else
    {
        pFormulaTrack = pNext;              // this cell was first cell
    }
    if ( pNext )
    {
        assert(pEOFormulaTrack != pCell);   // if this cell is also tail something's wrong
        pNext->SetPreviousTrack( pPrev );   // successor exists, set predecessor
    }
    else
    {
        pEOFormulaTrack = pPrev;            // this cell was last cell
    }
    pCell->SetPreviousTrack( nullptr );
    pCell->SetNextTrack( nullptr );
    --nFormulaTrackCount;
}

void ScDocument::FinalTrackFormulas( SfxHintId nHintId )
{
    mbTrackFormulasPending = false;
    mbFinalTrackFormulas = true;
    {
        ScBulkBroadcast aBulk( GetBASM(), nHintId);
        // Collect all pending formula cells in bulk.
        TrackFormulas( nHintId );
    }
    // A final round not in bulk to track all remaining formula cells and their
    // dependents that were collected during ScBulkBroadcast dtor.
    TrackFormulas( nHintId );
    mbFinalTrackFormulas = false;
}

/*
    The first is broadcasted,
    the ones that are created through this are appended to the Track by Notify.
    The next is broadcasted again, and so on.
    View initiates Interpret.
 */

void ScDocument::TrackFormulas( SfxHintId nHintId )
{
    if (!pBASM)
        return;

    if (pBASM->IsInBulkBroadcast() && !IsFinalTrackFormulas() &&
            (nHintId == SfxHintId::ScDataChanged || nHintId == SfxHintId::ScHiddenRowsChanged))
    {
        SetTrackFormulasPending();
        return;
    }

    if ( pFormulaTrack )
    {
        // outside the loop, check if any sheet has a "calculate" event script
        bool bCalcEvent = HasAnySheetEventScript( ScSheetEventId::CALCULATE, true );
        for( ScFormulaCell* pTrack = pFormulaTrack; pTrack != nullptr; pTrack = pTrack->GetNextTrack())
        {
            SCROW rowCount = 1;
            ScAddress address = pTrack->aPos;
            // Compress to include all adjacent cells in the same column.
            for(ScFormulaCell* pNext = pTrack->GetNextTrack(); pNext != nullptr; pNext = pNext->GetNextTrack())
            {
                if(pNext->aPos != ScAddress(address.Col(), address.Row() + rowCount, address.Tab()))
                    break;
                ++rowCount;
                pTrack = pNext;
            }
            ScHint aHint( nHintId, address, rowCount );
            BroadcastHintInternal( aHint );
            pBASM->AreaBroadcast( aHint );
            // for "calculate" event, keep track of which sheets are affected by tracked formulas
            if ( bCalcEvent )
                SetCalcNotification( address.Tab() );
        }
        bool bHaveForced = false;
        for( ScFormulaCell* pTrack = pFormulaTrack; pTrack != nullptr;)
        {
            ScFormulaCell* pNext = pTrack->GetNextTrack();
            RemoveFromFormulaTrack( pTrack );
            PutInFormulaTree( pTrack );
            if ( pTrack->GetCode()->IsRecalcModeForced() )
                bHaveForced = true;
            pTrack = pNext;
        }
        if ( bHaveForced )
        {
            SetForcedFormulas( true );
            if ( bAutoCalc && !IsAutoCalcShellDisabled() && !IsInInterpreter()
                    && !IsCalculatingFormulaTree() )
                CalcFormulaTree( true );
            else
                SetForcedFormulaPending( true );
        }
    }
    OSL_ENSURE( nFormulaTrackCount==0, "TrackFormulas: nFormulaTrackCount!=0" );
}

void ScDocument::StartAllListeners()
{
    sc::StartListeningContext aCxt(*this);
    for ( auto const & i: maTabs )
        if ( i )
            i->StartListeners(aCxt, true);
}

void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
        const ScRange& rRange, SCCOL nDx, SCROW nDy, SCTAB nDz
    )
{
    bool bExpandRefsOld = IsExpandRefs();
    if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) )
        SetExpandRefs(ScModule::get()->GetInputOptions().GetExpandRefs());
    if ( pBASM )
        pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz );
    SetExpandRefs( bExpandRefsOld );
}

void ScDocument::SetAutoCalc( bool bNewAutoCalc )
{
    bool bOld = bAutoCalc;
    bAutoCalc = bNewAutoCalc;
    if ( !bOld && bNewAutoCalc && bHasForcedFormulas )
    {
        if ( IsAutoCalcShellDisabled() )
            SetForcedFormulaPending( true );
        else if ( !IsInInterpreter() )
            CalcFormulaTree( true );
    }
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge