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


Quelle  chartlis.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 <memory>
#include <utility>
#include <vcl/svapp.hxx>

#include <chartlis.hxx>
#include <brdcst.hxx>
#include <document.hxx>
#include <reftokenhelper.hxx>
#include <formula/token.hxx>
#include <com/sun/star/chart/XChartDataChangeEventListener.hpp>

using namespace com::sun::star;
using ::std::vector;
using ::std::for_each;

// Update chart listeners quickly, to get a similar behavior to loaded charts
// which register UNO listeners.

class ScChartUnoData
{
    uno::Reference< chart::XChartDataChangeEventListener >  xListener;
    uno::Reference< chart::XChartData >                     xSource;

public:
            ScChartUnoData( uno::Reference< chart::XChartDataChangeEventListener > xL,
                            uno::Reference< chart::XChartData > xS ) :
                    xListener(std::move( xL )), xSource(std::move( xS )) {}

    const uno::Reference< chart::XChartDataChangeEventListener >& GetListener() const   { return xListener; }
    const uno::Reference< chart::XChartData >& GetSource() const                        { return xSource; }
};

// ScChartListener
ScChartListener::ExternalRefListener::ExternalRefListener(ScChartListener& rParent, ScDocument& rDoc) :
    mrParent(rParent), m_pDoc(&rDoc)
{
}

ScChartListener::ExternalRefListener::~ExternalRefListener()
{
    if (!m_pDoc || m_pDoc->IsInDtorClear())
        // The document is being destroyed.  Do nothing.
        return;

    // Make sure to remove all pointers to this object.
    m_pDoc->GetExternalRefManager()->removeLinkListener(this);
}

void ScChartListener::ExternalRefListener::notify(sal_uInt16 nFileId, ScExternalRefManager::LinkUpdateType eType)
{
    switch (eType)
    {
        case ScExternalRefManager::LINK_MODIFIED:
        {
            if (maFileIds.count(nFileId))
                // We are listening to this external document.  Send an update
                // request to the chart.
                mrParent.SetUpdateQueue();
        }
        break;
        case ScExternalRefManager::LINK_BROKEN:
            removeFileId(nFileId);
        break;
        case ScExternalRefManager::OH_NO_WE_ARE_GOING_TO_DIE:
            m_pDoc = nullptr;
        break;
    }
}

void ScChartListener::ExternalRefListener::addFileId(sal_uInt16 nFileId)
{
    maFileIds.insert(nFileId);
}

void ScChartListener::ExternalRefListener::removeFileId(sal_uInt16 nFileId)
{
    maFileIds.erase(nFileId);
}

ScChartListener::ScChartListener( OUString aName, ScDocument& rDocP,
        const ScRangeListRef& rRangeList ) :
    maName(std::move(aName)),
    mrDoc( rDocP ),
    bUsed( false ),
    bDirty( false )
{
    ScRefTokenHelper::getTokensFromRangeList(&rDocP, maTokens, *rRangeList);
}

ScChartListener::ScChartListener( OUString aName, ScDocument& rDocP, vector<ScTokenRef> aTokens ) :
    maTokens(std::move(aTokens)),
    maName(std::move(aName)),
    mrDoc( rDocP ),
    bUsed( false ),
    bDirty( false )
{
}

ScChartListener::~ScChartListener()
{
    if ( HasBroadcaster() )
        EndListeningTo();
    pUnoData.reset();

    if (mpExtRefListener)
    {
        // Stop listening to all external files.
        ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
        const std::unordered_set<sal_uInt16>& rFileIds = mpExtRefListener->getAllFileIds();
        for (const auto& rFileId : rFileIds)
            pRefMgr->removeLinkListener(rFileId, mpExtRefListener.get());
    }
}

void ScChartListener::SetUno(
        const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
        const uno::Reference< chart::XChartData >& rSource )
{
    pUnoData.reset( new ScChartUnoData( rListener, rSource ) );
}

uno::Reference< chart::XChartDataChangeEventListener > ScChartListener::GetUnoListener() const
{
    if ( pUnoData )
        return pUnoData->GetListener();
    return uno::Reference< chart::XChartDataChangeEventListener >();
}

uno::Reference< chart::XChartData > ScChartListener::GetUnoSource() const
{
    if ( pUnoData )
        return pUnoData->GetSource();
    return uno::Reference< chart::XChartData >();
}

void ScChartListener::Notify( const SfxHint& rHint )
{
    if (rHint.GetId() == SfxHintId::ScDataChanged)
        SetUpdateQueue();
}

void ScChartListener::Update()
{
    if ( mrDoc.IsInInterpreter() )
    {   // If interpreting do nothing and restart timer so we don't
        // interfere with interpreter and don't produce an Err522 or similar.
        // This may happen if we are rescheduled via Basic function.
        mrDoc.GetChartListenerCollection()->StartTimer();
        return ;
    }
    if ( pUnoData )
    {
        bDirty = false;
        // recognize some day what has changed inside the Chart
        chart::ChartDataChangeEvent aEvent( pUnoData->GetSource(),
                                        chart::ChartDataChangeType_ALL,
                                        0, 0, 0, 0 );
        pUnoData->GetListener()->chartDataChanged( aEvent );
    }
    else if ( mrDoc.GetAutoCalc() )
    {
        bDirty = false;
        mrDoc.UpdateChart(GetName());
    }
}

ScRangeListRef ScChartListener::GetRangeList() const
{
    ScRangeListRef aRLRef(new ScRangeList);
    ScRefTokenHelper::getRangeListFromTokens(&mrDoc, *aRLRef, maTokens, ScAddress());
    return aRLRef;
}

void ScChartListener::SetRangeList( const ScRangeListRef& rNew )
{
    vector<ScTokenRef> aTokens;
    ScRefTokenHelper::getTokensFromRangeList(&mrDoc, aTokens, *rNew);
    maTokens.swap(aTokens);
}

namespace {

class StartEndListening
{
public:
    StartEndListening(ScDocument& rDoc, ScChartListener& rParent, bool bStart) :
        mrDoc(rDoc), mrParent(rParent), mbStart(bStart) {}

    void operator() (const ScTokenRef& pToken)
    {
        if (!ScRefTokenHelper::isRef(pToken))
            return;

        bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
        if (bExternal)
        {
            sal_uInt16 nFileId = pToken->GetIndex();
            ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
            ScChartListener::ExternalRefListener* pExtRefListener = mrParent.GetExtRefListener();
            if (mbStart)
            {
                pRefMgr->addLinkListener(nFileId, pExtRefListener);
                pExtRefListener->addFileId(nFileId);
            }
            else
            {
                pRefMgr->removeLinkListener(nFileId, pExtRefListener);
                pExtRefListener->removeFileId(nFileId);
            }
        }
        else
        {
            ScRange aRange;
            ScRefTokenHelper::getRangeFromToken(&mrDoc, aRange, pToken, ScAddress(), bExternal);
            if (mbStart)
                startListening(aRange);
            else
                endListening(aRange);
        }
    }
private:
    void startListening(const ScRange& rRange)
    {
        if (rRange.aStart == rRange.aEnd)
            mrDoc.StartListeningCell(rRange.aStart, &mrParent);
        else
            mrDoc.StartListeningArea(rRange, false, &mrParent);
    }

    void endListening(const ScRange& rRange)
    {
        if (rRange.aStart == rRange.aEnd)
            mrDoc.EndListeningCell(rRange.aStart, &mrParent);
        else
            mrDoc.EndListeningArea(rRange, false, &mrParent);
    }
private:
    ScDocument& mrDoc;
    ScChartListener& mrParent;
    bool mbStart;
};

}

void ScChartListener::StartListeningTo()
{
    if (maTokens.empty())
        // no references to listen to.
        return;

    for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *thistrue));
}

void ScChartListener::EndListeningTo()
{
    if (maTokens.empty())
        // no references to listen to.
        return;

    for_each(maTokens.begin(), maTokens.end(), StartEndListening(mrDoc, *thisfalse));
}

void ScChartListener::ChangeListening( const ScRangeListRef& rRangeListRef,
                                       bool bDirtyP )
{
    EndListeningTo();
    SetRangeList( rRangeListRef );
    StartListeningTo();
    if ( bDirtyP )
        SetDirty( true );
}

void ScChartListener::UpdateChartIntersecting( const ScRange& rRange )
{
    ScTokenRef pToken;
    ScRefTokenHelper::getTokenFromRange(&mrDoc, pToken, rRange);

    if (ScRefTokenHelper::intersects(&mrDoc, maTokens, pToken, ScAddress()))
    {
        // force update (chart has to be loaded), don't use ScChartListener::Update
        mrDoc.UpdateChart(GetName());
    }
}

ScChartListener::ExternalRefListener* ScChartListener::GetExtRefListener()
{
    if (!mpExtRefListener)
        mpExtRefListener.reset(new ExternalRefListener(*this, mrDoc));

    return mpExtRefListener.get();
}

void ScChartListener::SetUpdateQueue()
{
    bDirty = true;
    mrDoc.GetChartListenerCollection()->StartTimer();
}

bool ScChartListener::operator==( const ScChartListener& r ) const
{
    bool b1 = !maTokens.empty();
    bool b2 = !r.maTokens.empty();

    if (&mrDoc != &r.mrDoc || bUsed != r.bUsed || bDirty != r.bDirty ||
        GetName() != r.GetName() || b1 != b2)
        return false;

    if (!b1 && !b2)
        // both token list instances are empty.
        return true;

    return maTokens == r.maTokens;
}

bool ScChartListener::operator!=( const ScChartListener& r ) const
{
    return !operator==(r);
}

ScChartHiddenRangeListener::ScChartHiddenRangeListener()
{
}

ScChartHiddenRangeListener::~ScChartHiddenRangeListener()
{
    // empty d'tor
}

void ScChartListenerCollection::Init()
{
    aIdle.SetInvokeHandler( LINK( this, ScChartListenerCollection, TimerHdl ) );
    aIdle.SetPriority( TaskPriority::REPAINT );
}

ScChartListenerCollection::ScChartListenerCollection( ScDocument& rDocP ) :
    meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
    aIdle( "sc::ScChartListenerCollection aIdle" ),
    rDoc( rDocP )
{
    Init();
}

ScChartListenerCollection::ScChartListenerCollection(
        const ScChartListenerCollection& rColl ) :
    meModifiedDuringUpdate( SC_CLCUPDATE_NONE ),
    aIdle( "sc::ScChartListenerCollection aIdle" ),
    rDoc( rColl.rDoc )
{
    Init();
}

ScChartListenerCollection::~ScChartListenerCollection()
{
    //  remove ChartListener objects before aIdle dtor is called, because
    //  ScChartListener::EndListeningTo may cause ScChartListenerCollection::StartTimer
    //  to be called if an empty ScNoteCell is deleted

    m_Listeners.clear();
}

void ScChartListenerCollection::StartAllListeners()
{
    for (auto const& it : m_Listeners)
    {
        it.second->StartListeningTo();
    }
}

bool ScChartListenerCollection::insert(ScChartListener* pListener)
{
    if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
        meModifiedDuringUpdate =  SC_CLCUPDATE_MODIFIED;
    OUString aName = pListener->GetName();
    return m_Listeners.insert(std::make_pair(aName, std::unique_ptr<ScChartListener>(pListener))).second;
}

void ScChartListenerCollection::removeByName(const OUString& rName)
{
    if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
        meModifiedDuringUpdate =  SC_CLCUPDATE_MODIFIED;
    m_Listeners.erase(rName);
}

ScChartListener* ScChartListenerCollection::findByName(const OUString& rName)
{
    ListenersType::iterator const it = m_Listeners.find(rName);
    return it == m_Listeners.end() ? nullptr : it->second.get();
}

const ScChartListener* ScChartListenerCollection::findByName(const OUString& rName) const
{
    ListenersType::const_iterator const it = m_Listeners.find(rName);
    return it == m_Listeners.end() ? nullptr : it->second.get();
}

bool ScChartListenerCollection::hasListeners() const
{
    return !m_Listeners.empty();
}

OUString ScChartListenerCollection::getUniqueName(std::u16string_view rPrefix) const
{
    for (sal_Int32 nNum = 1; nNum < 10000; ++nNum) // arbitrary limit to prevent infinite loop.
    {
        OUString aTestName = rPrefix + OUString::number(nNum);
        if (m_Listeners.find(aTestName) == m_Listeners.end())
            return aTestName;
    }
    return OUString();
}

void ScChartListenerCollection::ChangeListening( const OUString& rName,
        const ScRangeListRef& rRangeListRef )
{
    ScChartListener* pCL = findByName(rName);
    if (pCL)
    {
        pCL->EndListeningTo();
        pCL->SetRangeList( rRangeListRef );
    }
    else
    {
        pCL = new ScChartListener(rName, rDoc, rRangeListRef);
        insert(pCL);
    }
    pCL->StartListeningTo();
}

void ScChartListenerCollection::FreeUnused()
{
    if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
        meModifiedDuringUpdate =  SC_CLCUPDATE_MODIFIED;

    ListenersType aUsed;

    for (auto & pair : m_Listeners)
    {
        ScChartListener* p = pair.second.get();
        if (p->IsUno())
        {
            // We don't delete UNO charts; they are to be deleted separately via FreeUno().
            aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
            continue;
        }

        if (p->IsUsed())
        {
            p->SetUsed(false);
            aUsed.insert(std::make_pair(pair.first, std::move(pair.second)));
        }
    }

    m_Listeners = std::move(aUsed);
}

void ScChartListenerCollection::FreeUno( const uno::Reference< chart::XChartDataChangeEventListener >& rListener,
                                         const uno::Reference< chart::XChartData >& rSource )
{
    if (meModifiedDuringUpdate == SC_CLCUPDATE_RUNNING)
        meModifiedDuringUpdate =  SC_CLCUPDATE_MODIFIED;

    for (auto it = m_Listeners.begin(); it != m_Listeners.end(); )
    {
        ScChartListener *const p = it->second.get();
        if (p->IsUno() && p->GetUnoListener() == rListener && p->GetUnoSource() == rSource)
            it = m_Listeners.erase(it);
        else
            ++it;
    }
}

void ScChartListenerCollection::StartTimer()
{
    aIdle.Start();
}

IMPL_LINK_NOARG(ScChartListenerCollection, TimerHdl, Timer *, void)
{
    if ( Application::AnyInput( VclInputFlags::KEYBOARD ) )
    {
        aIdle.Start();
        return;
    }
    UpdateDirtyCharts();
}

void ScChartListenerCollection::UpdateDirtyCharts()
{
    // During ScChartListener::Update() the most nasty things can happen due to
    // UNO listeners, e.g. reentrant calls via BASIC to insert() and FreeUno()
    // and similar that modify m_Listeners and invalidate iterators.
    meModifiedDuringUpdate = SC_CLCUPDATE_RUNNING;

    for (auto const& it : m_Listeners)
    {
        ScChartListener *const p = it.second.get();
        if (p->IsDirty())
            p->Update();

        if (meModifiedDuringUpdate == SC_CLCUPDATE_MODIFIED)
            break;      // iterator is invalid

        if (aIdle.IsActive() && !rDoc.IsImportingXML())
            break;                      // one interfered
    }
    meModifiedDuringUpdate = SC_CLCUPDATE_NONE;
}

void ScChartListenerCollection::SetDirty()
{
    for (auto const& it : m_Listeners)
    {
        it.second->SetDirty(true);
    }

    StartTimer();
}

void ScChartListenerCollection::SetDiffDirty(
            const ScChartListenerCollection& rCmp, bool bSetChartRangeLists )
{
    bool bDirty = false;
    for (auto const& it : m_Listeners)
    {
        ScChartListener *const pCL = it.second.get();
        assert(pCL);
        const ScChartListener* pCLCmp = rCmp.findByName(pCL->GetName());
        if (!pCLCmp || *pCL != *pCLCmp)
        {
            if ( bSetChartRangeLists )
            {
                if (pCLCmp)
                {
                    const ScRangeListRef xList1 = pCL->GetRangeList();
                    const ScRangeListRef xList2 = pCLCmp->GetRangeList();
                    bool b1 = xList1.is();
                    bool b2 = xList2.is();
                    if ( b1 != b2 || (b1 && b2 && (*xList1 != *xList2)) )
                        rDoc.SetChartRangeList( pCL->GetName(), xList1 );
                }
                else
                    rDoc.SetChartRangeList( pCL->GetName(), pCL->GetRangeList() );
            }
            bDirty = true;
            pCL->SetDirty( true );
        }
    }
    if ( bDirty )
        StartTimer();
}

void ScChartListenerCollection::SetRangeDirty( const ScRange& rRange )
{
    bool bDirty = false;
    for (auto const& it : m_Listeners)
    {
        ScChartListener *const pCL = it.second.get();
        const ScRangeListRef xList = pCL->GetRangeList();
        if ( xList.is() && xList->Intersects( rRange ) )
        {
            bDirty = true;
            pCL->SetDirty( true );
        }
    }
    if ( bDirty )
        StartTimer();

    // New hidden range listener implementation
    for (auto& [pListener, rHiddenRange] : maHiddenListeners)
    {
        if (rHiddenRange.Intersects(rRange))
        {
            pListener->notify();
        }
    }
}

void ScChartListenerCollection::UpdateChartsContainingTab( SCTAB nTab )
{
    ScRange aRange( 0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab );
    for (auto const& it : m_Listeners)
    {
        it.second->UpdateChartIntersecting(aRange);
    }
}

bool ScChartListenerCollection::operator==( const ScChartListenerCollection& r ) const
{
    // Do not use ScStrCollection::operator==() here that uses IsEqual and Compare.
    // Use ScChartListener::operator==() instead.
    if (&rDoc != &r.rDoc)
        return false;

    return std::equal(m_Listeners.begin(), m_Listeners.end(), r.m_Listeners.begin(), r.m_Listeners.end(),
        [](const ListenersType::value_type& lhs, const ListenersType::value_type& rhs) {
            return (lhs.first == rhs.first) && (*lhs.second == *rhs.second);
        });
}

void ScChartListenerCollection::StartListeningHiddenRange( const ScRange& rRange, ScChartHiddenRangeListener* pListener )
{
    maHiddenListeners.insert(std::make_pair<>(pListener, rRange));
}

void ScChartListenerCollection::EndListeningHiddenRange( ScChartHiddenRangeListener* pListener )
{
    auto range = maHiddenListeners.equal_range(pListener);
    maHiddenListeners.erase(range.first, range.second);
}

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

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

¤ Dauer der Verarbeitung: 0.16 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