Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/data/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 226 kB image not shown  

Quelle  document.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 <scitems.hxx>

#include <editeng/boxitem.hxx>
#include <editeng/editobj.hxx>
#include <svx/svditer.hxx>
#include <sfx2/docfile.hxx>
#include <svl/numformat.hxx>
#include <poolcach.hxx>
#include <svl/zforlist.hxx>
#include <unotools/charclass.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <tools/urlobj.hxx>
#include <sal/log.hxx>
#include <osl/diagnose.h>

#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
#include <com/sun/star/sheet/TablePageBreakData.hpp>
#include <com/sun/star/lang/NotInitializedException.hpp>

#include <document.hxx>
#include <docsh.hxx>
#include <docuno.hxx>
#include <table.hxx>
#include <column.hxx>
#include <attrib.hxx>
#include <attarray.hxx>
#include <patattr.hxx>
#include <rangenam.hxx>
#include <poolhelp.hxx>
#include <docpool.hxx>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <dbdata.hxx>
#include <chartlis.hxx>
#include <rangelst.hxx>
#include <markdata.hxx>
#include <drwlayer.hxx>
#include <validat.hxx>
#include <prnsave.hxx>
#include <chgtrack.hxx>
#include <hints.hxx>
#include <detdata.hxx>
#include <dpobject.hxx>
#include <scmod.hxx>
#include <dociter.hxx>
#include <progress.hxx>
#include <autonamecache.hxx>
#include <bcaslot.hxx>
#include <postit.hxx>
#include <clipparam.hxx>
#include <defaultsoptions.hxx>
#include <editutil.hxx>
#include <stringutil.hxx>
#include <formulaiter.hxx>
#include <formulacell.hxx>
#include <clipcontext.hxx>
#include <listenercontext.hxx>
#include <scopetools.hxx>
#include <refupdatecontext.hxx>
#include <formulagroup.hxx>
#include <tokenstringcontext.hxx>
#include <compressedarray.hxx>
#include <recursionhelper.hxx>
#include <SparklineGroup.hxx>
#include <SparklineList.hxx>
#include <undomanager.hxx>

#include <formula/vectortoken.hxx>

#include <limits>
#include <memory>
#include <utility>
#include <unordered_map>

#include <comphelper/lok.hxx>

#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>

#include <mtvelements.hxx>
#include <sfx2/lokhelper.hxx>

using ::editeng::SvxBorderLine;
using namespace ::com::sun::star;

namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::sheet::TablePageBreakData;
using ::std::set;

namespace {

std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
{
    SCTAB nTabStart = MAXTAB;
    SCTAB nTabEnd = 0;
    SCTAB nMax = static_cast<SCTAB>(rTables.size());
    for (const auto& rTab : rMark)
    {
        if (rTab >= nMax)
            break;

        if (!rTables[rTab])
            continue;

        if (rTab < nTabStart)
            nTabStart = rTab;
        nTabEnd = rTab;
    }

    return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
}

void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
{
    EventDescription aDescription;
    aDescription.aID = "grid_window";
    aDescription.aAction = rAction;
    aDescription.aParameters = std::move(aParameters);
    aDescription.aParent = "MainWindow";
    aDescription.aKeyWord = "ScGridWinUIObject";

    UITestLogger::getInstance().logEvent(aDescription);
}

struct ScDefaultAttr
{
    SCROW                   nFirst { 0 };
    SCSIZE                  nCount { 0 };
};

}

typedef std::unordered_map<const ScPatternAttr*, ScDefaultAttr> ScDefaultAttrMap;

void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
{
    if (!ValidTab(nTab) || HasTable(nTab))
        return;

    // Get Custom prefix
    const ScDefaultsOptions& rOpt = ScModule::get()->GetDefaultsOptions();
    OUString aString = rOpt.GetInitTabPrefix() + OUString::number(nTab+1);
    if ( _bNeedsNameCheck )
        CreateValidTabName( aString );  // no doubles
    if (nTab < GetTableCount())
    {
        maTabs[nTab].reset( new ScTable(*this, nTab, aString) );
    }
    else
    {
        while (nTab > GetTableCount())
            maTabs.push_back(nullptr);
        maTabs.emplace_back( new ScTable(*this, nTab, aString) );
    }
    maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
}

bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
    {
        rHashCode = pTable->GetHashCode();
        return true;
    }
    return false;
}

bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
    {
        rName = pTable->GetName();
        return true;
    }
    rName.clear();
    return false;
}

const OUString & ScDocument::GetCopyTabName( SCTAB nTab ) const
{
    if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
        return maTabNames[nTab];
    return EMPTY_OUSTRING;
}

bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
{
    if (ScTable* pTable = FetchTable(nTab))
    {
        pTable->SetCodeName(rName);
        return true;
    }
    SAL_WARN("sc",  "can't set code name " << rName );
    return false;
}

bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
    {
        rName = pTable->GetCodeName();
        return true;
    }
    rName.clear();
    return false;
}

bool ScDocument::SetTotalsRowBelow( SCTAB nTab, bool bVal )
{
    if (ScTable* pTable = FetchTable(nTab))
    {
        pTable->SetTotalsRowBelow(bVal);
        return true;
    }
    return false;
}

bool ScDocument::GetTotalsRowBelow( SCTAB nTab ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
    {
        return pTable->GetTotalsRowBelow();
    }
    return true;
}

bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
{
    static OUString aCacheName, aCacheUpperName;

    assert(!IsThreadedGroupCalcInProgress());
    if (aCacheName != rName)
    {
        aCacheName = rName;
        // surprisingly slow ...
        aCacheUpperName = ScGlobal::getCharClass().uppercase(rName);
    }
    const OUString aUpperName = aCacheUpperName;

    for (SCTAB i = 0; i < GetTableCount(); i++)
        if (maTabs[i])
        {
            if (aUpperName == maTabs[i]->GetUpperName())
            {
                rTab = i;
                return true;
            }
        }
    rTab = 0;
    return false;
}

std::vector<OUString> ScDocument::GetAllTableNames() const
{
    std::vector<OUString> aNames;
    aNames.reserve(maTabs.size());
    for (const auto& a : maTabs)
    {
        // Positions need to be preserved for ScCompiler and address convention
        // context, so still push an empty string for NULL tabs.
        OUString aName;
        if (a)
        {
            const ScTable& rTab = *a;
            aName = rTab.GetName();
        }
        aNames.push_back(aName);
    }

    return aNames;
}

ScDBData* ScDocument::GetAnonymousDBData(SCTAB nTab)
{
    if (ScTable* pTable = FetchTable(nTab))
        return pTable->GetAnonymousDBData();
    return nullptr;
}

SCTAB ScDocument::GetTableCount() const
{
    return static_cast<SCTAB>(maTabs.size());
}

void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
{
    if (ScTable* pTable = FetchTable(nTab))
        pTable->SetAnonymousDBData(std::move(pDBData));
}

void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
{
    mpAnonymousDBData = std::move(pDBData);
}

ScDBData* ScDocument::GetAnonymousDBData()
{
    return mpAnonymousDBData.get();
}

bool ScDocument::ValidTabName( const OUString& rName )
{
    if (rName.isEmpty())
        return false;
    sal_Int32 nLen = rName.getLength();

#if 1
    // Restrict sheet names to what Excel accepts.
    /* TODO: We may want to remove this restriction for full ODFF compliance.
     * Merely loading and calculating ODF documents using these characters in
     * sheet names is not affected by this, but all sheet name editing and
     * copying functionality is, maybe falling back to "Sheet4" or similar. */

    for (sal_Int32 i = 0; i < nLen; ++i)
    {
        const sal_Unicode c = rName[i];
        switch (c)
        {
            case ':':
            case '\\':
            case '/':
            case '?':
            case '*':
            case '[':
            case ']':
                // these characters are not allowed to match XL's convention.
                return false;
            case '\'':
                if (i == 0 || i == nLen - 1)
                    // single quote is not allowed at the first or last
                    // character position.
                    return false;
            break;
        }
    }
#endif

    return true;
}

bool ScDocument::ValidNewTabName( const OUString& rName ) const
{
    bool bValid = ValidTabName(rName);
    if (!bValid)
        return false;
    OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
    for (const auto& a : maTabs)
    {
        if (!a)
            continue;
        const OUString& rOldName = a->GetUpperName();
        bValid = rOldName != aUpperName;
        if (!bValid)
            break;
    }
    return bValid;
}

void ScDocument::CreateValidTabName(OUString& rName) const
{
    if ( !ValidTabName(rName) )
    {
        // Find new one

        // Get Custom prefix
        const ScDefaultsOptions& rOpt = ScModule::get()->GetDefaultsOptions();
        const OUString aStrTable = rOpt.GetInitTabPrefix();

        bool         bOk   = false;

        // First test if the prefix is valid, if so only avoid doubles
        bool bPrefix = ValidTabName( aStrTable );
        OSL_ENSURE(bPrefix, "Invalid Table Name");
        SCTAB nDummy;

        for (SCTAB i = GetTableCount() + 1; !bOk ; i++)
        {
            rName = aStrTable + OUString::number(static_cast<sal_Int32>(i));
            if (bPrefix)
                bOk = ValidNewTabName( rName );
            else
                bOk = !GetTable( rName, nDummy );
        }
    }
    else
    {
        // testing the supplied Name

        if ( !ValidNewTabName(rName) )
        {
            SCTAB i = 1;
            OUString aName;
            do
            {
                i++;
                aName = rName + "_" + OUString::number(static_cast<sal_Int32>(i));
            }
            while (!ValidNewTabName(aName) && (i < MAXTAB+1));
            rName = aName;
        }
    }
}

void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
{
    aNames.clear();//ensure that the vector is empty

    // Get Custom prefix
    const ScDefaultsOptions& rOpt = ScModule::get()->GetDefaultsOptions();
    const OUString aStrTable = rOpt.GetInitTabPrefix();

    OUStringBuffer rName;

    // First test if the prefix is valid, if so only avoid doubles
    bool bPrefix = ValidTabName( aStrTable );
    OSL_ENSURE(bPrefix, "Invalid Table Name");
    SCTAB nDummy;
    SCTAB i = GetTableCount() + 1;

    for (SCTAB j = 0; j < nCount; ++j)
    {
        bool bOk = false;
        while(!bOk)
        {
            rName = aStrTable;
            rName.append(static_cast<sal_Int32>(i));
            if (bPrefix)
                bOk = ValidNewTabName( rName.toString() );
            else
                bOk = !GetTable( rName.toString(), nDummy );
            i++;
        }
        aNames.push_back(rName.makeStringAndClear());
    }
}

void ScDocument::AppendTabOnLoad(const OUString& rName)
{
    SCTAB nTabCount = GetTableCount();
    if (!ValidTab(nTabCount))
        // max table count reached.  No more tables.
        return;

    OUString aName = rName;
    CreateValidTabName(aName);
    maTabs.emplace_back( new ScTable(*this, nTabCount, aName) );
}

void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
{
    if (!ValidTab(nTab) || GetTableCount() <= nTab)
        return;

    if (!ValidTabName(rName))
        return;

    maTabs[nTab]->SetName(rName);
}

void ScDocument::InvalidateStreamOnSave()
{
    for (const auto& a : maTabs)
    {
        if (a)
            a->SetStreamValid(false);
    }
}

bool ScDocument::InsertTab(
    SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
{
    // auto-accept any in-process input to prevent move the cell into next sheet in online.
    if (comphelper::LibreOfficeKit::isActive())
        if (ScModule* mod = ScModule::get(); !mod->IsFormulaMode())
            mod->InputEnterHandler();

    SCTAB nTabCount = GetTableCount();
    bool bValid = ValidTab(nTabCount);
    if ( !bExternalDocument )   // else test rName == "'Doc'!Tab" first
        bValid = (bValid && ValidNewTabName(rName));
    if (bValid)
    {
        if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
        {
            nPos = maTabs.size();
            maTabs.emplace_back( new ScTable(*this, nTabCount, rName) );
            if ( bExternalDocument )
                maTabs[nTabCount]->SetVisible( false );
        }
        else
        {
            if (ValidTab(nPos) && (nPos < nTabCount))
            {
                sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);

                ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
                xColNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,1 );
                xRowNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,1 );
                if (pRangeName)
                    pRangeName->UpdateInsertTab(aCxt);
                pDBCollection->UpdateReference(
                                    URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
                if (pDPCollection)
                    pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
                if (pDetOpList)
                    pDetOpList->UpdateReference( *this, URM_INSDEL, aRange, 0,0,1 );
                UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
                UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
                if ( pUnoBroadcaster )
                    pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );

                for (const auto& a : maTabs)
                {
                    if (a)
                        a->UpdateInsertTab(aCxt);
                }
                maTabs.emplace(maTabs.begin() + nPos, new ScTable(*this, nPos, rName));

                // UpdateBroadcastAreas must be called between UpdateInsertTab,
                // which ends listening, and StartAllListeners, to not modify
                // areas that are to be inserted by starting listeners.
                UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
                for (const auto& a : maTabs)
                {
                    if (a)
                        a->UpdateCompile();
                }

                StartAllListeners();

                if (pValidationList)
                {
                    ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
                    pValidationList->UpdateInsertTab(aCxt);
                }

                bValid = true;
            }
            else
                bValid = false;
        }
    }

    if (bValid)
    {
        sc::SetFormulaDirtyContext aCxt;
        aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
        aCxt.mnTabDeletedStart = nPos;
        aCxt.mnTabDeletedEnd = nPos;
        SetAllFormulasDirty(aCxt);

        if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
        {
            ScModelObj* pModel = GetDocumentShell()->GetModel();
            SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
        }
    }

    return bValid;
}

bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
            bool bNamesValid )
{
    SCTAB nNewSheets = static_cast<SCTAB>(rNames.size());
    SCTAB nTabCount = GetTableCount();
    bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);

    if (bValid)
    {
        if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
        {
            for ( SCTAB i = 0; i < nNewSheets; ++i )
            {
                maTabs.emplace_back( new ScTable(*this, nTabCount + i, rNames.at(i)) );
            }
        }
        else
        {
            if (ValidTab(nPos) && (nPos < nTabCount))
            {
                sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
                ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
                xColNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,nNewSheets );
                xRowNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,nNewSheets );
                if (pRangeName)
                    pRangeName->UpdateInsertTab(aCxt);
                pDBCollection->UpdateReference(
                                    URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
                if (pDPCollection)
                    pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
                if (pDetOpList)
                    pDetOpList->UpdateReference( *this, URM_INSDEL, aRange, 0,0,nNewSheets );
                UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
                UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
                if ( pUnoBroadcaster )
                    pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );

                for (const auto& a : maTabs)
                {
                    if (a)
                        a->UpdateInsertTab(aCxt);
                }
                for (SCTAB i = 0; i < nNewSheets; ++i)
                {
                    maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(*this, nPos + i, rNames.at(i)) );
                }

                // UpdateBroadcastAreas must be called between UpdateInsertTab,
                // which ends listening, and StartAllListeners, to not modify
                // areas that are to be inserted by starting listeners.
                UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
                for (const auto& a : maTabs)
                {
                    if (a)
                        a->UpdateCompile();
                }

                StartAllListeners();

                if (pValidationList)
                {
                    ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
                    pValidationList->UpdateInsertTab(aCxt);
                }

                bValid = true;
            }
            else
                bValid = false;
        }
    }

    if (bValid)
    {
        sc::SetFormulaDirtyContext aCxt;
        SetAllFormulasDirty(aCxt);
    }

    return bValid;
}

bool ScDocument::DeleteTab( SCTAB nTab )
{
    bool bValid = false;
    if (HasTable(nTab))
    {
        SCTAB nTabCount = GetTableCount();
        if (nTabCount > 1)
        {
            sc::AutoCalcSwitch aACSwitch(*thisfalse);
            sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
            sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);

            ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab );
            DelBroadcastAreasInRange( aRange );

            // #i8180# remove database ranges etc. that are on the deleted tab
            // (restored in undo with ScRefUndoData)

            xColNameRanges->DeleteOnTab( nTab );
            xRowNameRanges->DeleteOnTab( nTab );
            pDBCollection->DeleteOnTab( nTab );
            if (pDPCollection)
                pDPCollection->DeleteOnTab( nTab );
            if (pDetOpList)
                pDetOpList->DeleteOnTab( nTab );
            DeleteAreaLinksOnTab( nTab );

            // normal reference update

            aRange.aEnd.SetTab(GetTableCount() - 1);
            xColNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,-1 );
            xRowNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,-1 );
            if (pRangeName)
                pRangeName->UpdateDeleteTab(aCxt);
            pDBCollection->UpdateReference(
                                URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
            if (pDPCollection)
                pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
            if (pDetOpList)
                pDetOpList->UpdateReference( *this, URM_INSDEL, aRange, 0,0,-1 );
            UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
            UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
            if (pValidationList)
            {
                ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
                pValidationList->UpdateDeleteTab(aCxt);
            }
            if ( pUnoBroadcaster )
                pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );

            for (auto & pTab : maTabs)
                if (pTab)
                    pTab->UpdateDeleteTab(aCxt);

            // tdf#149502 make sure ScTable destructor called after the erase is finished, when
            // maTabs[x].nTab==x is true again, as it should be always true.
            // In the end of maTabs.erase, maTabs indexes change, but nTab updated before erase.
            // ~ScTable expect that maTabs[x].nTab==x so it shouldn't be called during erase.
            ScTableUniquePtr pErasedTab = std::move(maTabs[nTab]);
            maTabs.erase(maTabs.begin() + nTab);
            delete pErasedTab.release();

            // UpdateBroadcastAreas must be called between UpdateDeleteTab,
            // which ends listening, and StartAllListeners, to not modify
            // areas that are to be inserted by starting listeners.
            UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
            for (const auto& a : maTabs)
            {
                if (a)
                    a->UpdateCompile();
            }
            // Excel-Filter deletes some Tables while loading, Listeners will
            // only be triggered after the loading is done.
            if ( !bInsertingFromOtherDoc )
            {
                StartAllListeners();

                sc::SetFormulaDirtyContext aFormulaDirtyCxt;
                SetAllFormulasDirty(aFormulaDirtyCxt);
            }

            if (comphelper::LibreOfficeKit::isActive())
            {
                ScModelObj* pModel = GetDocumentShell()->GetModel();
                SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
            }

            bValid = true;
        }
    }
    return bValid;
}

bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
{
    bool bValid = false;
    if (HasTable(nTab) && (nTab + nSheets) <= GetTableCount())
    {
        SCTAB nTabCount = GetTableCount();
        if (nTabCount > nSheets)
        {
            sc::AutoCalcSwitch aACSwitch(*thisfalse);
            sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
            sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);

            for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
            {
                ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab + aTab );
                DelBroadcastAreasInRange( aRange );

                // #i8180# remove database ranges etc. that are on the deleted tab
                // (restored in undo with ScRefUndoData)

                xColNameRanges->DeleteOnTab( nTab + aTab );
                xRowNameRanges->DeleteOnTab( nTab + aTab );
                pDBCollection->DeleteOnTab( nTab + aTab );
                if (pDPCollection)
                    pDPCollection->DeleteOnTab( nTab + aTab );
                if (pDetOpList)
                    pDetOpList->DeleteOnTab( nTab + aTab );
                DeleteAreaLinksOnTab( nTab + aTab );
            }

            if (pRangeName)
                pRangeName->UpdateDeleteTab(aCxt);

            // normal reference update

            ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTabCount - 1 );
            xColNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,-1*nSheets );
            xRowNameRanges->UpdateReference( URM_INSDEL, *this, aRange, 0,0,-1*nSheets );
            pDBCollection->UpdateReference(
                                URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
            if (pDPCollection)
                pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
            if (pDetOpList)
                pDetOpList->UpdateReference( *this, URM_INSDEL, aRange, 0,0,-1*nSheets );
            UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
            UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
            if (pValidationList)
            {
                ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
                pValidationList->UpdateDeleteTab(aCxt);
            }
            if ( pUnoBroadcaster )
                pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );

            for (auto & pTab : maTabs)
                if (pTab)
                    pTab->UpdateDeleteTab(aCxt);

            maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
            // UpdateBroadcastAreas must be called between UpdateDeleteTab,
            // which ends listening, and StartAllListeners, to not modify
            // areas that are to be inserted by starting listeners.
            UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
            for (const auto& a : maTabs)
            {
                if (a)
                    a->UpdateCompile();
            }
            // Excel-Filter deletes some Tables while loading, Listeners will
            // only be triggered after the loading is done.
            if ( !bInsertingFromOtherDoc )
            {
                StartAllListeners();

                sc::SetFormulaDirtyContext aFormulaDirtyCxt;
                SetAllFormulasDirty(aFormulaDirtyCxt);
            }

            if (comphelper::LibreOfficeKit::isActive())
            {
                ScModelObj* pModel = GetDocumentShell()->GetModel();
                SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
            }

            bValid = true;
        }
    }
    return bValid;
}

bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
{
    bool bValid = false;
    SCTAB i;
    if (HasTable(nTab))
    {
        if ( bExternalDocument )
            bValid = true;      // composed name
        else
            bValid = ValidTabName(rName);
        for (i = 0; i < GetTableCount() && bValid; i++)
        {
            if (maTabs[i] && (i != nTab))
            {
                OUString aOldName = maTabs[i]->GetName();
                bValid = !ScGlobal::GetTransliteration().isEqual( rName, aOldName );
            }
        }
        if (bValid)
        {
            // #i75258# update charts before renaming, so they can get their live data objects.
            // Once the charts are live, the sheet can be renamed without problems.
            if ( pChartListenerCollection )
                pChartListenerCollection->UpdateChartsContainingTab( nTab );
            maTabs[nTab]->SetName(rName);

            // If formulas refer to the renamed sheet, the TokenArray remains valid,
            // but the XML stream must be re-generated.
            for (const auto& pTable : maTabs)
            {
                if (pTable)
                {
                    pTable->SetStreamValid( false );
                    // tdf#156815 Reset solver settings so next time they're loaded they come with
                    // the updated sheet name
                    pTable->ResetSolverSettings();
                }
            }

            if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
            {
                ScModelObj* pModel = GetDocumentShell()->GetModel();
                SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
            }
        }
    }

    collectUIInformation({{"NewName", rName}}, u"Rename_Sheet"_ustr);

    return bValid;
}

void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
{
    if (ScTable* pTable = FetchTable(nTab))
        pTable->SetVisible(bVisible);
}

bool ScDocument::IsVisible( SCTAB nTab ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->IsVisible();
    return false;
}

bool ScDocument::IsStreamValid( SCTAB nTab ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->IsStreamValid();
    return false;
}

void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
{
    if (ScTable* pTable = FetchTable(nTab))
        pTable->SetStreamValid( bSet, bIgnoreLock );
}

void ScDocument::LockStreamValid( bool bLock )
{
    mbStreamValidLocked = bLock;
}

bool ScDocument::IsPendingRowHeights( SCTAB nTab ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->IsPendingRowHeights();
    return false;
}

void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet )
{
    if (ScTable* pTable = FetchTable(nTab))
        pTable->SetPendingRowHeights(bSet);
}

sal_uInt16 ScDocument::GetSheetOptimalMinRowHeight(SCTAB nTab) const
{
    const ScTable* pTab = FetchTable(nTab);
    if (!pTab)
        return ScGlobal::nStdRowHeight;

    return pTab->GetOptimalMinRowHeight();
}

void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling)
{
    ScTable* pTable = FetchTable(nTab);
    if (!pTable)
        return;

    if ( bImportingXML )
    {
        // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
        // is applied in SetImportingXML(false). This is so the shapes can be loaded in
        // normal LTR mode.

        pTable->SetLoadingRTL( bRTL );
        return;
    }

    pTable->SetLayoutRTL( bRTL );     // only sets the flag
    pTable->SetDrawPageSize(truetrue, eObjectHandling);

    //  objects are already repositioned via SetDrawPageSize, only writing mode is missing
    if (!mpDrawLayer)
        return;

    SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
    OSL_ENSURE(pPage,"Page ?");
    if (!pPage)
        return;

    SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
    SdrObject* pObject = aIter.Next();
    while (pObject)
    {
        pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
        pObject = aIter.Next();
    }
}

bool ScDocument::IsLayoutRTL( SCTAB nTab ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->IsLayoutRTL();

    return false;
}

bool ScDocument::IsNegativePage( SCTAB nTab ) const
{
    //  Negative page area is always used for RTL layout.
    //  The separate method is used to find all RTL handling of drawing objects.
    return IsLayoutRTL( nTab );
}

/* ----------------------------------------------------------------------------
    used search area:

    GetCellArea  - Only Data
    GetTableArea - Data / Attributes
    GetPrintArea - intended for character objects,
                    sweeps attributes all the way to bottom / right
---------------------------------------------------------------------------- */


bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
{
    if (HasTable(nTab))
        return maTabs[nTab]->GetCellArea(rEndCol, rEndRow);

    rEndCol = 0;
    rEndRow = 0;
    return false;
}

bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->GetTableArea(rEndCol, rEndRow, bCalcHiddens);

    rEndCol = 0;
    rEndRow = 0;
    return false;
}

bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
{
    if (!HasTable(nTab))
        return false;

    SCCOL nCol1, nCol2;
    SCROW nRow1, nRow2;
    maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
    maTabs[nTab]->GetLastDataPos(nCol2, nRow2);

    if (nCol1 > nCol2 || nRow1 > nRow2)
        // invalid range.
        return false;

    // Make sure the area only shrinks, and doesn't grow.
    if (rStartCol < nCol1)
        rStartCol = nCol1;
    if (nCol2 < rEndCol)
        rEndCol = nCol2;
    if (rStartRow < nRow1)
        rStartRow = nRow1;
    if (nRow2 < rEndRow)
        rEndRow = nRow2;

    if (rStartCol > rEndCol || rStartRow > rEndRow)
        // invalid range.
        return false;

    return true;  // success!
}

bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
        SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
        bool bStickyTopRow, bool bStickyLeftCol, ScDataAreaExtras* pDataAreaExtras ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
    {
        return pTable->ShrinkToUsedDataArea(
            o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, bColumnsOnly, bStickyTopRow,
            bStickyLeftCol, pDataAreaExtras);
    }
    o_bShrunk = false;
    return false;
}

SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->GetLastDataRow(nCol1, nCol2, nLastRow);
    return -1;
}

// connected area

void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
                              SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
{
    if (const ScTable* pTable = FetchTable(nTab))
        pTable->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
}

void ScDocument::GetBackColorArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
                                   SCCOL& rEndCol, SCROW& rEndRow ) const
{
    if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
        maTabs[nTab]->GetBackColorArea( rStartCol, rStartRow, rEndCol, rEndRow );
}

bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const
{
    SCTAB nTab = rRange.aStart.Tab();
    if (nTab != rRange.aEnd.Tab())
        return true;

    if (const ScTable* pTable = FetchTable(nTab))
        return pTable->GetDataAreaSubrange(rRange);

    return true;
}

void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
                                    SCCOL& rEndCol, SCROW& rEndRow )
{
    if (ScTable* pTable = FetchTable(nTab))
        pTable->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow);
}

void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList )
{
    ScRangeListRef aNew = new ScRangeList;
    if (rRangeList.is())
    {
        for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
        {
            ScRange aRange( (*rRangeList)[i] );
            if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MaxCol() ) ||
                 ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MaxRow() ) )
            {
                SCCOL nStartCol = aRange.aStart.Col();
                SCROW nStartRow = aRange.aStart.Row();
                SCCOL nEndCol = aRange.aEnd.Col();
                SCROW nEndRow = aRange.aEnd.Row();
                SCTAB nTab = aRange.aStart.Tab();
                if (ScTable* pTable = FetchTable(nTab))
                    pTable->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
                aRange.aStart.SetCol( nStartCol );
                aRange.aStart.SetRow( nStartRow );
                aRange.aEnd.SetCol( nEndCol );
                aRange.aEnd.SetRow( nEndRow );
            }
            aNew->push_back(aRange);
        }
    }
    else
    {
        OSL_FAIL("LimitChartIfAll: Ref==0");
    }
    rRangeList = std::move(aNew);
}

static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
{
    // without ScMarkData, leave start/end unchanged
    if ( !pTabMark )
        return;

    for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
        if (pTabMark->GetTableSelect(nTab))
        {
            // find first range of consecutive selected sheets
            rTabRangeStart = pTabMark->GetFirstSelected();
            while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
                ++nTab;
            rTabRangeEnd = nTab;
            return;
        }
}

static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
{
    if ( pTabMark )
    {
        // find next range of consecutive selected sheets after rTabRangeEnd
        for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
            if (pTabMark->GetTableSelect(nTab))
            {
                rTabRangeStart = nTab;
                while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
                    ++nTab;
                rTabRangeEnd = nTab;
                return true;
            }
    }
    return false;
}

bool ScDocument::CanInsertRow( const ScRange& rRange ) const
{
    SCCOL nStartCol = rRange.aStart.Col();
    SCROW nStartRow = rRange.aStart.Row();
    SCTAB nStartTab = rRange.aStart.Tab();
    SCCOL nEndCol = rRange.aEnd.Col();
    SCROW nEndRow = rRange.aEnd.Row();
    SCTAB nEndTab = rRange.aEnd.Tab();
    PutInOrder( nStartCol, nEndCol );
    PutInOrder( nStartRow, nEndRow );
    PutInOrder( nStartTab, nEndTab );
    SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);

    bool bTest = true;
    for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
        if (maTabs[i])
            bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );

    return bTest;
}

namespace {

struct SetDirtyIfPostponedHandler
{
    void operator() (const ScTableUniquePtr & p)
    {
        if (p)
            p->SetDirtyIfPostponed();
    }
};

struct BroadcastRecalcOnRefMoveHandler
{
    void operator() (const ScTableUniquePtr & p)
    {
        if (p)
            p->BroadcastRecalcOnRefMove();
    }
};

struct BroadcastRecalcOnRefMoveGuard
{
    explicit BroadcastRecalcOnRefMoveGuard( ScDocument* pDoc ) :
        aSwitch( *pDoc, false),
        aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
    {
    }

private:
    sc::AutoCalcSwitch aSwitch; // first for ctor/dtor order, destroy second
    ScBulkBroadcast aBulk;      // second for ctor/dtor order, destroy first
};

}

bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
                            SCCOL nEndCol,   SCTAB nEndTab,
                            SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
                            const ScMarkData* pTabMark )
{
    SCTAB i;

    PutInOrder( nStartCol, nEndCol );
    PutInOrder( nStartTab, nEndTab );
    if ( pTabMark )
    {
        nStartTab = 0;
        nEndTab = GetTableCount() - 1;
    }

    bool bTest = true;
    bool bRet = false;
    bool bOldAutoCalc = GetAutoCalc();
    SetAutoCalc( false );   // avoid multiple calculations
    bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
    EnableDelayDeletingBroadcasters( true );
    for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
        if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
            bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
    if (bTest)
    {
        // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
        // aren't shifted that would be rebuild at UpdateReference

        // handle chunks of consecutive selected sheets together
        SCTAB nTabRangeStart = nStartTab;
        SCTAB nTabRangeEnd = nEndTab;
        lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
        ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
        sc::EndListeningContext aEndListenCxt(*this);

        std::vector<ScAddress> aGroupPos;
        do
        {
            aShiftedRange.aStart.SetTab(nTabRangeStart);
            aShiftedRange.aEnd.SetTab(nTabRangeEnd);

            // Collect all formula groups that will get split by the shifting,
            // and end all their listening.  Record the position of the top
            // cell of the topmost group, and the position of the bottom cell
            // of the bottommost group.
            EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);

            UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
        }
        while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

        lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());

        sc::RefUpdateContext aCxt(*this);
        aCxt.meMode = URM_INSDEL;
        aCxt.maRange = aShiftedRange;
        aCxt.mnRowDelta = nSize;
        do
        {
            aCxt.maRange.aStart.SetTab(nTabRangeStart);
            aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
            UpdateReference(aCxt, pRefUndoDoc, false);        // without drawing objects
        }
        while (lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

        // UpdateReference should have set "needs listening" flags to those
        // whose references have been modified.  We also need to set this flag
        // to those that were in the groups that got split by shifting.
        SetNeedsListeningGroups(aGroupPos);

        for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
        {
            if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
            {
                maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
                maTabs[i]->CommentNotifyAddressChange(nStartCol, nStartRow, nEndCol, MaxRow());
            }
        }

        //  UpdateRef for drawing layer must be after inserting,
        //  when the new row heights are known.
        for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
            if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
                maTabs[i]->UpdateDrawRef( URM_INSDEL,
                            nStartCol, nStartRow, nStartTab, nEndCol, MaxRow(), nEndTab,
                            0, static_cast<SCROW>(nSize), 0 );

        if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
        {   // A new Listening is needed when references to deleted ranges are restored,
            // previous Listeners were removed in FormulaCell UpdateReference.
            StartAllListeners();
        }
        else
        {   // Listeners have been removed in UpdateReference
            StartNeededListeners();

            // At least all cells using range names pointing relative to the
            // moved range must be recalculated, and all cells marked postponed
            // dirty.
            for (const auto& a : maTabs)
            {
                if (a)
                    a->SetDirtyIfPostponed();
            }

            {
                BroadcastRecalcOnRefMoveGuard g(this);
                std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
            }
        }
        bRet = true;
    }
    EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
    SetAutoCalc( bOldAutoCalc );
    if ( bRet && pChartListenerCollection )
        pChartListenerCollection->UpdateDirtyCharts();
    return bRet;
}

bool ScDocument::InsertRow( const ScRange& rRange )
{
    return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
                      rRange.aEnd.Col(),   rRange.aEnd.Tab(),
                      rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
}

void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
                            SCCOL nEndCol,   SCTAB nEndTab,
                            SCROW nStartRow, SCSIZE nSize,
                            ScDocument* pRefUndoDoc, bool* pUndoOutline,
                            const ScMarkData* pTabMark )
{
    SCTAB i;

    PutInOrder( nStartCol, nEndCol );
    PutInOrder( nStartTab, nEndTab );
    if ( pTabMark )
    {
        nStartTab = 0;
        nEndTab = GetTableCount() - 1;
    }

    sc::AutoCalcSwitch aACSwitch(*thisfalse); // avoid multiple calculations

    // handle chunks of consecutive selected sheets together
    SCTAB nTabRangeStart = nStartTab;
    SCTAB nTabRangeEnd = nEndTab;
    lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
    do
    {
        if ( ValidRow(nStartRow+nSize) )
        {
            DelBroadcastAreasInRange( ScRange(
                ScAddress( nStartCol, nStartRow, nTabRangeStart ),
                ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
            UpdateBroadcastAreas( URM_INSDEL, ScRange(
                ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
                ScAddress( nEndCol, MaxRow(), nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
        }
        else
            DelBroadcastAreasInRange( ScRange(
                ScAddress( nStartCol, nStartRow, nTabRangeStart ),
                ScAddress( nEndCol, MaxRow(), nTabRangeEnd ) ) );
    }
    while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

    sc::RefUpdateContext aCxt(*this);
    const bool bLastRowIncluded = (static_cast<SCROW>(nStartRow + nSize) == GetMaxRowCount() && ValidRow(nStartRow));
    if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
    {
        lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
        aCxt.meMode = URM_INSDEL;
        aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
        if (bLastRowIncluded)
        {
            // Last row is included, shift a virtually non-existent row in.
            aCxt.maRange = ScRange( nStartCol, GetMaxRowCount(), nTabRangeStart, nEndCol, GetMaxRowCount(), nTabRangeEnd);
        }
        else
        {
            aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
        }
        do
        {
            UpdateReference(aCxt, pRefUndoDoc, truefalse);
        }
        while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
    }

    if (pUndoOutline)
        *pUndoOutline = false;

    // Keep track of the positions of all formula groups that have been joined
    // during row deletion.
    std::vector<ScAddress> aGroupPos;

    for ( i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
    {
        if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
        {
            maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
            maTabs[i]->CommentNotifyAddressChange(nStartCol, nStartRow, nEndCol, MaxRow());
        }
    }

    // Newly joined groups have some of their members still listening.  We
    // need to make sure none of them are listening.
    EndListeningGroups(aGroupPos);

    // Mark all joined groups for group listening.
    SetNeedsListeningGroups(aGroupPos);

    if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
    {
        // Listeners have been removed in UpdateReference
        StartNeededListeners();

        // At least all cells using range names pointing relative to the moved
        // range must be recalculated, and all cells marked postponed dirty.
        for (const auto& a : maTabs)
        {
            if (a)
                a->SetDirtyIfPostponed();
        }

        {
            BroadcastRecalcOnRefMoveGuard g(this);
            std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
        }
    }

    if (pChartListenerCollection)
        pChartListenerCollection->UpdateDirtyCharts();
}

void ScDocument::DeleteRow( const ScRange& rRange )
{
    DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
               rRange.aEnd.Col(),   rRange.aEnd.Tab(),
               rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
}

bool ScDocument::CanInsertCol( const ScRange& rRange ) const
{
    SCCOL nStartCol = rRange.aStart.Col();
    SCROW nStartRow = rRange.aStart.Row();
    SCTAB nStartTab = rRange.aStart.Tab();
    SCCOL nEndCol = rRange.aEnd.Col();
    SCROW nEndRow = rRange.aEnd.Row();
    SCTAB nEndTab = rRange.aEnd.Tab();
    PutInOrder( nStartCol, nEndCol );
    PutInOrder( nStartRow, nEndRow );
    PutInOrder( nStartTab, nEndTab );
    SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);

    bool bTest = true;
    for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
        if (maTabs[i])
            bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );

    return bTest;
}

bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
                            SCROW nEndRow,   SCTAB nEndTab,
                            SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
                            const ScMarkData* pTabMark )
{
    SCTAB i;

    PutInOrder( nStartRow, nEndRow );
    PutInOrder( nStartTab, nEndTab );
    if ( pTabMark )
    {
        nStartTab = 0;
        nEndTab = GetTableCount() - 1;
    }

    bool bTest = true;
    bool bRet = false;
    bool bOldAutoCalc = GetAutoCalc();
    SetAutoCalc( false );   // avoid multiple calculations
    bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
    EnableDelayDeletingBroadcasters( true );
    for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
        if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
            bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
    if (bTest)
    {
        // handle chunks of consecutive selected sheets together
        SCTAB nTabRangeStart = nStartTab;
        SCTAB nTabRangeEnd = nEndTab;
        lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
        do
        {
            UpdateBroadcastAreas( URM_INSDEL, ScRange(
                ScAddress( nStartCol, nStartRow, nTabRangeStart ),
                ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
        }
        while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

        lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());

        sc::RefUpdateContext aCxt(*this);
        aCxt.meMode = URM_INSDEL;
        aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MaxCol(), nEndRow, nTabRangeEnd);
        aCxt.mnColDelta = nSize;
        do
        {
            UpdateReference(aCxt, pRefUndoDoc, truefalse);
        }
        while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

        for (i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
            if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
                maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);

        if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
        {   // A new Listening is needed when references to deleted ranges are restored,
            // previous Listeners were removed in FormulaCell UpdateReference.
            StartAllListeners();
        }
        else
        {
            // Listeners have been removed in UpdateReference
            StartNeededListeners();
            // At least all cells using range names pointing relative to the
            // moved range must be recalculated, and all cells marked postponed
            // dirty.
            {
                ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
                std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
            }
            // Cells containing functions such as CELL, COLUMN or ROW may have
            // changed their values on relocation. Broadcast them.
            {
                BroadcastRecalcOnRefMoveGuard g(this);
                std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
            }
        }
        bRet = true;
    }
    EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
    SetAutoCalc( bOldAutoCalc );
    if ( bRet && pChartListenerCollection )
        pChartListenerCollection->UpdateDirtyCharts();
    return bRet;
}

bool ScDocument::InsertCol( const ScRange& rRange )
{
    return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
                      rRange.aEnd.Row(),   rRange.aEnd.Tab(),
                      rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
}

void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
                                SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
                                bool* pUndoOutline, const ScMarkData* pTabMark )
{
    SCTAB i;

    PutInOrder( nStartRow, nEndRow );
    PutInOrder( nStartTab, nEndTab );
    if ( pTabMark )
    {
        nStartTab = 0;
        nEndTab = GetTableCount() - 1;
    }

    sc::AutoCalcSwitch aACSwitch(*thisfalse); // avoid multiple calculations
    ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);

    // handle chunks of consecutive selected sheets together
    SCTAB nTabRangeStart = nStartTab;
    SCTAB nTabRangeEnd = nEndTab;
    lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
    do
    {
        if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
        {
            DelBroadcastAreasInRange( ScRange(
                ScAddress( nStartCol, nStartRow, nTabRangeStart ),
                ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
            UpdateBroadcastAreas( URM_INSDEL, ScRange(
                ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
                ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
        }
        else
            DelBroadcastAreasInRange( ScRange(
                ScAddress( nStartCol, nStartRow, nTabRangeStart ),
                ScAddress( MaxCol(), nEndRow, nTabRangeEnd ) ) );
    }
    while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));

    sc::RefUpdateContext aCxt(*this);
    const bool bLastColIncluded = (static_cast<SCCOL>(nStartCol + nSize) == GetMaxColCount() && ValidCol(nStartCol));
    if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
    {
        lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
        aCxt.meMode = URM_INSDEL;
        aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
        if (bLastColIncluded)
        {
            // Last column is included, shift a virtually non-existent column in.
            aCxt.maRange = ScRange( GetMaxColCount(), nStartRow, nTabRangeStart, GetMaxColCount(), nEndRow, nTabRangeEnd);
        }
        else
        {
            aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
                    MaxCol(), nEndRow, nTabRangeEnd);
        }
        do
        {
            UpdateReference(aCxt, pRefUndoDoc, truefalse);
        }
        while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
    }

    if (pUndoOutline)
        *pUndoOutline = false;

    for (i = nStartTab; i <= nEndTab && i < GetTableCount(); ++i)
    {
        if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
            maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
    }

    if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
    {
        // Listeners have been removed in UpdateReference
        StartNeededListeners();

        // At least all cells using range names pointing relative to the moved
        // range must be recalculated, and all cells marked postponed dirty.
        for (const auto& a : maTabs)
        {
            if (a)
                a->SetDirtyIfPostponed();
        }

        {
            BroadcastRecalcOnRefMoveGuard g(this);
            std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
        }
    }

    if (pChartListenerCollection)
        pChartListenerCollection->UpdateDirtyCharts();
}

void ScDocument::DeleteCol( const ScRange& rRange )
{
    DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(),
               rRange.aEnd.Row(),   rRange.aEnd.Tab(),
               rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
}

//  for Area-Links: Insert/delete cells, when the range is changed.
//  (without Paint)

static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew,
                            ScRange& rColRange, bool& rInsCol, bool& rDelCol,
                            ScRange& rRowRange, bool& rInsRow, bool& rDelRow )
{
    OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" );

    rInsCol = rDelCol = rInsRow = rDelRow = false;

    SCCOL nStartX = rOld.aStart.Col();
    SCROW nStartY = rOld.aStart.Row();
    SCCOL nOldEndX = rOld.aEnd.Col();
    SCROW nOldEndY = rOld.aEnd.Row();
    SCCOL nNewEndX = rNew.aEnd.Col();
    SCROW nNewEndY = rNew.aEnd.Row();
    SCTAB nTab = rOld.aStart.Tab();

    // if more rows, columns are inserted/deleted at the old height.
    bool bGrowY = ( nNewEndY > nOldEndY );
    SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY;
    SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX;

    // Columns

    if ( nNewEndX > nOldEndX )          // Insert columns
    {
        rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab );
        rInsCol = true;
    }
    else if ( nNewEndX < nOldEndX )     // Delete columns
    {
        rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab );
        rDelCol = true;
    }

    // Rows

    if ( nNewEndY > nOldEndY )          // Insert rows
    {
        rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab );
        rInsRow = true;
    }
    else if ( nNewEndY < nOldEndY )     // Delete rows
    {
        rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab );
        rDelRow = true;
    }
}

bool ScDocument::HasPartOfMerged( const ScRange& rRange )
{
    bool bPart = false;
    SCTAB nTab = rRange.aStart.Tab();

    SCCOL nStartX = rRange.aStart.Col();
    SCROW nStartY = rRange.aStart.Row();
    SCCOL nEndX = rRange.aEnd.Col();
    SCROW nEndY = rRange.aEnd.Row();

    if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab,
                        HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
    {
        ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
        ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab );

        bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() ||
                  nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() );
    }
    return bPart;
}

formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos )
{
    SCTAB nTab = rPos.Tab();
    if (ScTable* pTable = FetchTable(nTab))
        return pTable->ResolveStaticReference(rPos.Col(), rPos.Row());
    return formula::FormulaTokenRef();
}

formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange )
{
    SCTAB nTab = rRange.aStart.Tab();
    if (nTab != rRange.aEnd.Tab() || !HasTable(nTab))
        return formula::FormulaTokenRef();

    return maTabs[nTab]->ResolveStaticReference(
        rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
}

formula::VectorRefArray ScDocument::FetchVectorRefArray( const ScAddress& rPos, SCROW nLength )
{
    SCTAB nTab = rPos.Tab();
    if (ScTable* pTable = FetchTable(nTab))
        return pTable->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
    return formula::VectorRefArray();
}

#ifdef DBG_UTIL
void ScDocument::AssertNoInterpretNeeded( const ScAddress& rPos, SCROW nLength )
{
    SCTAB nTab = rPos.Tab();
    assert(HasTable(nTab));
    if (ScTable* pTable = FetchTable(nTab))
        return pTable->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
}
#endif

void ScDocument::UnlockAdjustHeight()
{
    assert(nAdjustHeightLock > 0);
    if(nAdjustHeightLock > 0)
        --nAdjustHeightLock;
}

bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength, const ScFormulaCellGroupRef& mxGroup, ScAddress* pDirtiedAddress)
{
    SCTAB nTab = rPos.Tab();
    if (ScTable* pTable = FetchTable(nTab))
    {
        bool bRet = pTable->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup, pDirtiedAddress);
        if (!bRet && pDirtiedAddress && pDirtiedAddress->Row() != -1)
        {
            pDirtiedAddress->SetCol(rPos.Col());
            pDirtiedAddress->SetTab(nTab);
        }
        return bRet;
    }
    return false;
}

bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
{
    if ( rOld == rNew )
        return true;

    bool bOk = true;
    bool bInsCol,bDelCol,bInsRow,bDelRow;
    ScRange aColRange,aRowRange;
    lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );

    if ( bInsCol && !CanInsertCol( aColRange ) )            // Cells at the edge ?
        bOk = false;
    if ( bInsRow && !CanInsertRow( aRowRange ) )            // Cells at the edge ?
        bOk = false;

    if ( bInsCol || bDelCol )
    {
        aColRange.aEnd.SetCol(MaxCol());
        if ( HasPartOfMerged(aColRange) )
            bOk = false;
    }
    if ( bInsRow || bDelRow )
    {
        aRowRange.aEnd.SetRow(MaxRow());
        if ( HasPartOfMerged(aRowRange) )
            bOk = false;
    }

    return bOk;
}

void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
{
    if (bClear)
        DeleteAreaTab( rOld, InsertDeleteFlags::ALL );

    bool bInsCol,bDelCol,bInsRow,bDelRow;
    ScRange aColRange,aRowRange;
    lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );

    if ( bInsCol )
        InsertCol( aColRange );         // First insert columns
    if ( bInsRow )
        InsertRow( aRowRange );

    if ( bDelRow )
        DeleteRow( aRowRange );         // First delete rows
    if ( bDelCol )
        DeleteCol( aColRange );

    // Expand references to inserted rows

    if ( bInsCol || bInsRow )
    {
        ScRange aGrowSource = rOld;
        aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
        aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
        SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
        SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
        UpdateGrow( aGrowSource, nGrowX, nGrowY );
    }
}

void ScDocument::DeleteArea(
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
    InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
{
    sc::AutoCalcSwitch aACSwitch(*thisfalse);

    PutInOrder( nCol1, nCol2 );
    PutInOrder( nRow1, nRow2 );

    std::vector<ScAddress> aGroupPos;
    // Destroy and reconstruct listeners only if content is affected.
    bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
    if (bDelContent)
    {
        // Record the positions of top and/or bottom formula groups that intersect
        // the area borders.
        sc::EndListeningContext aCxt(*this);
        ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
        for (SCTAB i = 0; i < GetTableCount(); i++)
        {
            if (rMark.GetTableSelect(i))
            {
                aRange.aStart.SetTab(i);
                aRange.aEnd.SetTab(i);

                EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
            }
        }
        aCxt.purgeEmptyBroadcasters();
    }

    for (SCTAB i = 0; i < GetTableCount(); i++)
        if (maTabs[i])
            if ( rMark.GetTableSelect(i) || bIsUndo )
                maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);

    if (!bDelContent)
        return;

    // Re-start listeners on those top bottom groups that have been split.
    SetNeedsListeningGroups(aGroupPos);
    StartNeededListeners();

--> --------------------

--> maximum size reached

--> --------------------

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

¤ Dauer der Verarbeitung: 0.25 Sekunden  ¤

*© 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.