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


Quelle  docfunc.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 <comphelper/lok.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <sfx2/app.hxx>
#include <editeng/editobj.hxx>
#include <editeng/justifyitem.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/bindings.hxx>
#include <utility>
#include <vcl/weld.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/svapp.hxx>
#include <svx/svdocapt.hxx>
#include <sal/log.hxx>
#include <unotools/charclass.hxx>
#include <osl/diagnose.h>

#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>

#include <docfunc.hxx>

#include <sc.hrc>
#include <strings.hrc>

#include <arealink.hxx>
#include <attrib.hxx>
#include <dociter.hxx>
#include <autoform.hxx>
#include <formulacell.hxx>
#include <cellmergeoption.hxx>
#include <detdata.hxx>
#include <detfunc.hxx>
#include <docpool.hxx>
#include <docsh.hxx>
#include <drwlayer.hxx>
#include <editutil.hxx>
#include <globstr.hrc>
#include <olinetab.hxx>
#include <patattr.hxx>
#include <rangenam.hxx>
#include <refundo.hxx>
#include <scresid.hxx>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <tablink.hxx>
#include <tabvwsh.hxx>
#include <uiitems.hxx>
#include <undoblk.hxx>
#include <undocell.hxx>
#include <undodraw.hxx>
#include <undotab.hxx>
#include <sizedev.hxx>
#include <scmod.hxx>
#include <inputhdl.hxx>
#include <editable.hxx>
#include <compiler.hxx>
#include <scui_def.hxx>
#include <tabprotection.hxx>
#include <clipparam.hxx>
#include <externalrefmgr.hxx>
#include <undorangename.hxx>
#include <progress.hxx>
#include <dpobject.hxx>
#include <stringutil.hxx>
#include <cellvalue.hxx>
#include <tokenarray.hxx>
#include <rowheightcontext.hxx>
#include <cellvalues.hxx>
#include <undoconvert.hxx>
#include <docfuncutil.hxx>
#include <sheetevents.hxx>
#include <conditio.hxx>
#include <columnspanset.hxx>
#include <validat.hxx>
#include <SparklineGroup.hxx>
#include <SparklineAttributes.hxx>
#include <SparklineData.hxx>
#include <undo/UndoInsertSparkline.hxx>
#include <undo/UndoDeleteSparkline.hxx>
#include <undo/UndoDeleteSparklineGroup.hxx>
#include <undo/UndoEditSparklineGroup.hxx>
#include <undo/UndoUngroupSparklines.hxx>
#include <undo/UndoGroupSparklines.hxx>
#include <undo/UndoEditSparkline.hxx>
#include <config_features.h>

#include <memory>
#include <basic/basmgr.hxx>
#include <set>
#include <vector>
#include <sfx2/viewfrm.hxx>

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

#define AUTOFORMAT_WARN_SIZE 0x10ffffUL

void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction)
{
    // #i101118# if drawing layer collects the undo actions, add it there
    ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer();
    if( pDrawLayer && pDrawLayer->IsRecording() )
        pDrawLayer->AddCalcUndo( std::move(pUndoAction) );
    else
        rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), rDocShell ) );
    rDocShell.SetDrawModified();

    // the affected sheet isn't known, so all stream positions are invalidated
    ScDocument& rDoc = rDocShell.GetDocument();
    SCTAB nTabCount = rDoc.GetTableCount();
    for (SCTAB nTab=0; nTab<nTabCount; nTab++)
        rDoc.SetStreamValid(nTab, false);
}

//  paint row above the range (because of lines after AdjustRowHeight)

static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
{
    SCROW nRow = rRange.aStart.Row();
    if ( nRow > 0 )
    {
        SCTAB nTab = rRange.aStart.Tab();   //! all of them?
        --nRow;
        ScDocument& rDoc = rDocShell.GetDocument();
        rDocShell.PostPaint( ScRange(0,nRow,nTab,rDoc.MaxCol(),nRow,nTab), PaintPartFlags::Grid );
    }
}

bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi )
{
    ScDocument& rDoc = rDocShell.GetDocument();
    SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false);
    if ( rDoc.IsImportingXML() )
    {
        //  for XML import, all row heights are updated together after importing
        return false;
    }
    if ( rDoc.IsAdjustHeightLocked() )
    {
        return false;
    }

    SCTAB nTab      = rRange.aStart.Tab();
    SCROW nStartRow = rRange.aStart.Row();
    SCROW nEndRow   = rRange.aEnd.Row();

    ScSizeDeviceProvider aProv( rDocShell );
    Fraction aOne(1,1);

    sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
    bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
    // tdf#76183: recalculate objects' positions
    if (bChanged)
    {
        if (comphelper::LibreOfficeKit::isActive())
        {
            SfxViewShell* pViewShell = SfxViewShell::GetFirst();
            while (pViewShell)
            {
                ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
                if (pTabViewShell && pSomeViewForThisDoc && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId())
                {
                    if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
                        pPosHelper->invalidateByIndex(nStartRow);
                }
                pViewShell = SfxViewShell::GetNext(*pViewShell);
            }
        }
        rDoc.SetDrawPageSize(nTab);
    }

    if ( bPaint && bChanged )
        rDocShell.PostPaint(ScRange(0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
                            PaintPartFlags::Grid | PaintPartFlags::Left);

    if (comphelper::LibreOfficeKit::isActive())
    {
        ScTabViewShell::notifyAllViewsHeaderInvalidation(pSomeViewForThisDoc, ROW_HEADER, nTab);
        ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
            pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
            false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab);
    }

    return bChanged;
}

bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos)
{
    ScDocShellModificator aModificator( rDocShell );

    rDocShell.MakeDrawLayer();
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo (rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).ShowPred( nCol, nRow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED );
        rDoc.AddDetectiveOperation( aOperation );
        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), &aOperation ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos)
{
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo(rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    if (!pModel)
        return false;

    ScDocShellModificator aModificator( rDocShell );

    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).DeletePred( nCol, nRow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED );
        rDoc.AddDetectiveOperation( aOperation );
        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), &aOperation ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos)
{
    ScDocShellModificator aModificator( rDocShell );

    rDocShell.MakeDrawLayer();
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo(rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).ShowSucc( nCol, nRow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC );
        rDoc.AddDetectiveOperation( aOperation );
        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), &aOperation ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos)
{
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo (rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    if (!pModel)
        return false;

    ScDocShellModificator aModificator( rDocShell );

    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteSucc( nCol, nRow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC );
        rDoc.AddDetectiveOperation( aOperation );
        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), &aOperation ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveAddError(const ScAddress& rPos)
{
    ScDocShellModificator aModificator( rDocShell );

    rDocShell.MakeDrawLayer();
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo (rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    SCCOL nCol = rPos.Col();
    SCROW nRow = rPos.Row();
    SCTAB nTab = rPos.Tab();

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).ShowError( nCol, nRow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR );
        rDoc.AddDetectiveOperation( aOperation );
        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), &aOperation ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab)
{
    ScDocShellModificator aModificator( rDocShell );

    rDocShell.MakeDrawLayer();
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo (rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();

    std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent()));
    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bOverflow;
    bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    xWaitWin.reset();
    if (bDone)
    {
        if (pUndo && bUndo)
        {
            pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) );
            rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) );
        }
        aModificator.SetDocumentModified();
        if ( bOverflow )
        {
            std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
                                                          VclMessageType::Info, VclButtonsType::Ok,
                                                          ScResId(STR_DETINVALID_OVERFLOW)));
            xInfoBox->run();
        }
    }

    return bDone;
}

bool ScDocFunc::DetectiveDelAll(SCTAB nTab)
{
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo (rDoc.IsUndoEnabled());
    ScDrawLayer* pModel = rDoc.GetDrawLayer();
    if (!pModel)
        return false;

    ScDocShellModificator aModificator( rDocShell );

    if (bUndo)
        pModel->BeginCalcUndo(false);
    bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Detective );
    std::unique_ptr<SdrUndoGroup> pUndo;
    if (bUndo)
        pUndo = pModel->GetCalcUndo();
    if (bDone)
    {
        ScDetOpList* pOldList = rDoc.GetDetOpList();
        std::unique_ptr<ScDetOpList> pUndoList;
        if (bUndo && pOldList)
            pUndoList.reset(new ScDetOpList(*pOldList));

        rDoc.ClearDetectiveOperations();

        if (bUndo)
        {
            rDocShell.GetUndoManager()->AddUndoAction(
                        std::make_unique<ScUndoDetective>( rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) );
        }
        aModificator.SetDocumentModified();
        SfxBindings* pBindings = rDocShell.GetViewBindings();
        if (pBindings)
            pBindings->Invalidate( SID_DETECTIVE_REFRESH );
    }

    return bDone;
}

bool ScDocFunc::DetectiveRefresh( bool bAutomatic )
{
    bool bDone = false;
    ScDocument& rDoc = rDocShell.GetDocument();

    ScDetOpList* pList = rDoc.GetDetOpList();
    if ( pList && pList->Count() )
    {
        rDocShell.MakeDrawLayer();
        ScDrawLayer* pModel = rDoc.GetDrawLayer();
        const bool bUndo (rDoc.IsUndoEnabled());
        if (bUndo)
            pModel->BeginCalcUndo(false);

        //  Delete in all sheets

        SCTAB nTabCount = rDoc.GetTableCount();
        for (SCTAB nTab=0; nTab<nTabCount; nTab++)
            ScDetectiveFunc( rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows );    // don't remove circles

        //  repeat

        size_t nCount = pList->Count();
        for (size_t i=0; i < nCount; ++i)
        {
            const ScDetOpData& rData = pList->GetObject(i);
            const ScAddress& aPos = rData.GetPos();
            ScDetectiveFunc aFunc( rDoc, aPos.Tab() );
            SCCOL nCol = aPos.Col();
            SCROW nRow = aPos.Row();
            switch (rData.GetOperation())
            {
                case SCDETOP_ADDSUCC:
                    aFunc.ShowSucc( nCol, nRow );
                    break;
                case SCDETOP_DELSUCC:
                    aFunc.DeleteSucc( nCol, nRow );
                    break;
                case SCDETOP_ADDPRED:
                     aFunc.ShowPred( nCol, nRow );
                     break;
                case SCDETOP_DELPRED:
                    aFunc.DeletePred( nCol, nRow );
                    break;
                case SCDETOP_ADDERROR:
                    aFunc.ShowError( nCol, nRow );
                    break;
                default:
                    OSL_FAIL("wrong operation in DetectiveRefresh");
            }
        }

        if (bUndo)
        {
            std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
            if (pUndo)
            {
                pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) );
                // associate with the last action
                rDocShell.GetUndoManager()->AddUndoAction(
                                                std::make_unique<ScUndoDraw>( std::move(pUndo), rDocShell ),
                                                    bAutomatic );
            }
        }
        rDocShell.SetDrawModified();
        bDone = true;
    }
    return bDone;
}

static void lcl_collectAllPredOrSuccRanges(
    const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell,
    bool bPred)
{
    ScDocument& rDoc = rDocShell.GetDocument();
    vector<ScTokenRef> aRefTokens;
    if (rSrcRanges.empty())
        return;
    ScRange const & rFrontRange = rSrcRanges.front();
    ScDetectiveFunc aDetFunc(rDoc, rFrontRange.aStart.Tab());
    for (ScRange const & r : rSrcRanges)
    {
        if (bPred)
        {
            aDetFunc.GetAllPreds(
                r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
        }
        else
        {
            aDetFunc.GetAllSuccs(
                r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
        }
    }
    rRefTokens.swap(aRefTokens);
}

void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
{
    lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true);
}

void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
{
    lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false);
}

bool ScDocFunc::DeleteContents(
    const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
{
    ScDocShellModificator aModificator( rDocShell );

    if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
    {
        OSL_FAIL("ScDocFunc::DeleteContents without markings");
        return false;
    }

    ScDocument& rDoc = rDocShell.GetDocument();

    if (bRecord && !rDoc.IsUndoEnabled())
        bRecord = false;

    ScEditableTester aTester( rDoc, rMark );
    if (!aTester.IsEditable())
    {
        if (!bApi)
            rDocShell.ErrorMessage(aTester.GetMessageId());
        return false;
    }

    ScMarkData aMultiMark = rMark;
    aMultiMark.SetMarking(false);       // for MarkToMulti

    ScDocumentUniquePtr pUndoDoc;
    bool bMulti = aMultiMark.IsMultiMarked();
    aMultiMark.MarkToMulti();
    const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
    ScRange aExtendedRange(aMarkRange);
    if ( rDoc.ExtendMerge( aExtendedRange, true ) )
        bMulti = false;

    // no objects on protected tabs
    bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);

    sal_uInt16 nExtFlags = 0;       // extra flags are needed only if attributes are deleted
    if ( nFlags & InsertDeleteFlags::ATTRIB )
        rDocShell.UpdatePaintExt( nExtFlags, aMarkRange );

    //  order of operations:
    //  1) BeginDrawUndo
    //  2) Delete objects (DrawUndo will be filled)
    //  3) Copy content for undo and set up undo actions
    //  4) Delete content

    bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);
    if (bRecord && bDrawUndo)
        rDoc.BeginDrawUndo();

    if (bObjects)
    {
        if (bMulti)
            rDoc.DeleteObjectsInSelection( aMultiMark );
        else
            rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
                                       aMarkRange.aEnd.Col(),   aMarkRange.aEnd.Row(),
                                       aMultiMark );
    }

    // To keep track of all non-empty cells within the deleted area.
    std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;

    if ( bRecord )
    {
        pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti);
        pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange);
    }

    rDoc.DeleteSelection( nFlags, aMultiMark );

    // add undo action after drawing undo is complete (objects and note captions)
    if( bRecord )
    {
        sc::DocFuncUtil::addDeleteContentsUndo(
            rDocShell.GetUndoManager(), rDocShell, aMultiMark, aExtendedRange,
            std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo);
    }

    if (!AdjustRowHeight( aExtendedRange, true, bApi ))
        rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
    else if (nExtFlags & SC_PF_LINES)
        lcl_PaintAbove( rDocShell, aExtendedRange );    // for lines above the range

    aModificator.SetDocumentModified();

    return true;
}

tools::Long ScDocShell::GetTwipWidthHint(const ScAddress& rPos)
{
    ScViewData* pViewData = GetViewData();
    if (!pViewData)
        return -1;

    ScSizeDeviceProvider aProv(*this);
    Fraction aZoomX, aZoomY;
    double nPPTX, nPPTY;
    pViewData->setupSizeDeviceProviderForColWidth(aProv, aZoomX, aZoomY, nPPTX, nPPTY);

    ScDocument& rDoc = GetDocument();
    tools::Long nWidth = rDoc.GetNeededSize(rPos.Col(), rPos.Row(), rPos.Tab(), aProv.GetDevice(),
                                            nPPTX, nPPTY, aZoomX, aZoomY, true /*bWidth*/);

    return (nWidth + 2) / nPPTX; // same as ScColumn::GetOptimalColWidth
}

bool ScDocFunc::DeleteCell(
    const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
{
    ScDocShellModificator aModificator(rDocShell);

    ScDocument& rDoc = rDocShell.GetDocument();

    if (bRecord && !rDoc.IsUndoEnabled())
        bRecord = false;

    ScEditableTester aTester(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
    if (!aTester.IsEditable())
    {
        rDocShell.ErrorMessage(aTester.GetMessageId());
        return false;
    }

    // no objects on protected tabs
    bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);

    sal_uInt16 nExtFlags = 0;       // extra flags are needed only if attributes are deleted
    if (nFlags & InsertDeleteFlags::ATTRIB)
        rDocShell.UpdatePaintExt(nExtFlags, ScRange(rPos));

    //  order of operations:
    //  1) BeginDrawUndo
    //  2) delete objects (DrawUndo is filled)
    //  3) copy contents for undo
    //  4) delete contents
    //  5) add undo-action

    bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);     // needed for shown notes
    if (bDrawUndo && bRecord)
        rDoc.BeginDrawUndo();

    if (bObjects)
        rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);

    // To keep track of all non-empty cells within the deleted area.
    std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;

    ScDocumentUniquePtr pUndoDoc;
    if (bRecord)
    {
        pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, ScRange(rPos), nFlags, false);
        pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, ScRange(rPos));
    }

    tools::Long nBefore(rDocShell.GetTwipWidthHint(rPos));
    rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags);

    if (bRecord)
    {
        sc::DocFuncUtil::addDeleteContentsUndo(
            rDocShell.GetUndoManager(), rDocShell, rMark, ScRange(rPos), std::move(pUndoDoc),
            nFlags, pDataSpans, false, bDrawUndo);
    }

    if (!AdjustRowHeight(ScRange(rPos), true, bApi))
        rDocShell.PostPaint(
            rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(),
            PaintPartFlags::Grid, nExtFlags, nBefore);

    aModificator.SetDocumentModified();

    return true;
}

bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
                                    bool bApi )
{
    ScDocShellModificator aModificator( rDocShell );

    ScDocument& rDoc = rDocShell.GetDocument();
    bool bRecord = true;
    if (!rDoc.IsUndoEnabled())
        bRecord = false;

    ScEditableTester aTester( rDoc, rMark );
    if (!aTester.IsEditable())
    {
        if (!bApi)
            rDocShell.ErrorMessage(aTester.GetMessageId());
        return false;
    }

    ScMarkData aMultiMark = rMark;
    aMultiMark.SetMarking(false);       // for MarkToMulti
    aMultiMark.MarkToMulti();
    const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();

    if (bRecord)
    {
        SCTAB nStartTab = aMarkRange.aStart.Tab();
        SCTAB nTabCount = rDoc.GetTableCount();

        ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
        pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
        for (const auto& rTab : rMark)
        {
            if (rTab >= nTabCount)
                break;

            if (rTab != nStartTab)
                pUndoDoc->AddUndoTab( rTab, rTab );
        }

        ScRange aCopyRange = aMarkRange;
        aCopyRange.aStart.SetTab(0);
        aCopyRange.aEnd.SetTab(nTabCount-1);
        rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark);

        rDocShell.GetUndoManager()->AddUndoAction(
            std::make_unique<ScUndoTransliterate>( rDocShell, aMultiMark, std::move(pUndoDoc), nType ) );
    }

    rDoc.TransliterateText( aMultiMark, nType );

    if (!AdjustRowHeight( aMarkRange, truetrue ))
        rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid );

    aModificator.SetDocumentModified();

    return true;
}

bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();

    bool bUndo(rDoc.IsUndoEnabled());
    ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
    if (!aTester.IsEditable())
    {
        if (!bApi)
            rDocShell.ErrorMessage(aTester.GetMessageId());
        return false;
    }

    bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT);
    ScUndoEnterData::ValuesType aOldValues;

    if (bUndo)
    {
        ScUndoEnterData::Value aOldValue;

        aOldValue.mnTab = rPos.Tab();
        aOldValue.maCell.assign(rDoc, rPos);

        const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() );
        if ( const SfxUInt32Item* pItem = pPattern->GetItemSet().GetItemIfSet(
                                ATTR_VALUE_FORMAT,false) )
        {
            aOldValue.mbHasFormat = true;
            aOldValue.mnFormat = pItem->GetValue();
        }
        else
            aOldValue.mbHasFormat = false;

        aOldValues.push_back(aOldValue);
    }

    tools::Long nBefore(rDocShell.GetTwipWidthHint(rPos));
    o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText );
    tools::Long nAfter(rDocShell.GetTwipWidthHint(rPos));

    if (bUndo)
    {
        //  because of ChangeTracking, UndoAction can be created only after SetString was called
        rDocShell.GetUndoManager()->AddUndoAction(
            std::make_unique<ScUndoEnterData>(rDocShell, rPos, aOldValues, rText, nullptr));
    }

    if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) )
        AdjustRowHeight( ScRange(rPos), true, bApi );

    rDocShell.PostPaintCell( rPos, std::max(nBefore, nAfter) );
    aModificator.SetDocumentModified();

    // notify input handler here the same way as in PutCell
    if (bApi)
        NotifyInputHandler( rPos );

    const SfxUInt32Item* pItem = rDoc.GetAttr(rPos, ATTR_VALIDDATA);
    const ScValidationData* pData = rDoc.GetValidationEntry(pItem->GetValue());
    if (pData)
    {
        ScRefCellValue aCell(rDoc, rPos);
        if (pData->IsDataValid(aCell, rPos))
            ScDetectiveFunc(rDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
    }

    return true;
}

bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo = rDoc.IsUndoEnabled();

    bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);

    ScCellValue aOldVal;
    if (bUndo)
        aOldVal.assign(rDoc, rPos);

    rDoc.SetValue(rPos, fVal);

    if (bUndo)
    {
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        ScCellValue aNewVal;
        aNewVal.assign(rDoc, rPos);
        pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(rDocShell, rPos, aOldVal, aNewVal));
    }

    if (bHeight)
        AdjustRowHeight(ScRange(rPos), true, !bInteraction);

    rDocShell.PostPaintCell( rPos );
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction )
{
    ScDocument& rDoc = rDocShell.GetDocument();

    // Check for invalid range.
    SCROW nLastRow = rPos.Row() + aVals.size() - 1;
    if (nLastRow > rDoc.MaxRow())
        // out of bound.
        return;

    ScRange aRange(rPos);
    aRange.aEnd.SetRow(nLastRow);

    ScDocShellModificator aModificator(rDocShell);

    if (rDoc.IsUndoEnabled())
    {
        std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(rDocShell, rPos));
        rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues());
        pUndoObj->SetNewValues(aVals);
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        pUndoMgr->AddUndoAction(std::move(pUndoObj));
    }

    rDoc.SetValues(rPos, aVals);

    rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler(rPos);
}

bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo = rDoc.IsUndoEnabled();

    bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);

    ScCellValue aOldVal;
    if (bUndo)
        aOldVal.assign(rDoc, rPos);

    ScSetStringParam aParam;
    aParam.setTextInput();
    rDoc.SetString(rPos, rStr, &aParam);

    if (bUndo)
    {
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        ScCellValue aNewVal;
        aNewVal.assign(rDoc, rPos);
        pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(rDocShell, rPos, aOldVal, aNewVal));
    }

    if (bHeight)
        AdjustRowHeight(ScRange(rPos), true, !bInteraction);

    rDocShell.PostPaintCell( rPos );
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo = rDoc.IsUndoEnabled();

    bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);

    ScCellValue aOldVal;
    if (bUndo)
        aOldVal.assign(rDoc, rPos);

    rDoc.SetEditText(rPos, rStr.Clone());

    if (bUndo)
    {
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        ScCellValue aNewVal;
        aNewVal.assign(rDoc, rPos);
        pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(rDocShell, rPos, aOldVal, aNewVal));
    }

    if (bHeight)
        AdjustRowHeight(ScRange(rPos), true, !bInteraction);

    rDocShell.PostPaintCell( rPos );
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
{
    ScDocument& rDoc = rDocShell.GetDocument();

    if (ScStringUtil::isMultiline(rStr))
    {
        ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
        rEngine.SetTextCurrentDefaults(rStr);
        std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
        return SetEditCell(rPos, *pEditText, bInteraction);
    }
    else
        return SetStringCell(rPos, rStr, bInteraction);
}

bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction )
{
    std::unique_ptr<ScFormulaCell> xCell(pCell);

    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bUndo = rDoc.IsUndoEnabled();

    bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);

    ScCellValue aOldVal;
    if (bUndo)
        aOldVal.assign(rDoc, rPos);

    pCell = rDoc.SetFormulaCell(rPos, xCell.release());

    // For performance reasons API calls may disable calculation while
    // operating and recalculate once when done. If through user interaction
    // and AutoCalc is disabled, calculate the formula (without its
    // dependencies) once so the result matches the current document's content.
    if (bInteraction && !rDoc.GetAutoCalc() && pCell)
    {
        // calculate just the cell once and set Dirty again
        pCell->Interpret();
        pCell->SetDirtyVar();
        rDoc.PutInFormulaTree( pCell);
    }

    if (bUndo)
    {
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        ScCellValue aNewVal;
        aNewVal.assign(rDoc, rPos);
        pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(rDocShell, rPos, aOldVal, aNewVal));
    }

    if (bHeight)
        AdjustRowHeight(ScRange(rPos), true, !bInteraction);

    rDocShell.PostPaintCell( rPos );
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>&&nbsp;rCells, bool bInteraction )
{
    ScDocument& rDoc = rDocShell.GetDocument();

    const size_t nLength = rCells.size();
    if (rPos.Row() + nLength - 1 > o3tl::make_unsigned(rDoc.MaxRow()))
        // out of bound
        return false;

    ScRange aRange(rPos);
    aRange.aEnd.IncRow(nLength - 1);

    ScDocShellModificator aModificator( rDocShell );
    bool bUndo = rDoc.IsUndoEnabled();

    std::unique_ptr<sc::UndoSetCells> pUndoObj;
    if (bUndo)
    {
        pUndoObj.reset(new sc::UndoSetCells(rDocShell, rPos));
        rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues());
    }

    rDoc.SetFormulaCells(rPos, rCells);

    // For performance reasons API calls may disable calculation while
    // operating and recalculate once when done. If through user interaction
    // and AutoCalc is disabled, calculate the formula (without its
    // dependencies) once so the result matches the current document's content.
    if (bInteraction && !rDoc.GetAutoCalc())
    {
        for (auto* pCell : rCells)
        {
            // calculate just the cell once and set Dirty again
            pCell->Interpret();
            pCell->SetDirtyVar();
            rDoc.PutInFormulaTree( pCell);
        }
    }

    if (bUndo)
    {
        pUndoObj->SetNewValues(rCells);
        SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
        pUndoMgr->AddUndoAction(std::move(pUndoObj));
    }

    rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
    aModificator.SetDocumentModified();

    // #103934#; notify editline and cell in edit mode
    if (!bInteraction)
        NotifyInputHandler( rPos );

    return true;
}

void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
{
    ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
    if ( !(pViewSh && &pViewSh->GetViewData().GetDocShell() == &rDocShell) )
        return;

    ScInputHandler* pInputHdl = ScModule::get()->GetInputHdl();
    if ( pInputHdl && pInputHdl->GetCursorPos() == rPos )
    {
        bool bIsEditMode(pInputHdl->IsEditMode());

        // set modified if in editmode, because so the string is not set in the InputWindow like in the cell
        // (the cell shows the same like the InputWindow)
        if (bIsEditMode)
            pInputHdl->SetModified();
        pViewSh->UpdateInputHandler(false, !bIsEditMode);
    }
}

namespace {

    struct ScMyRememberItem
    {
        sal_Int32   nIndex;
        SfxItemSet  aItemSet;

        ScMyRememberItem(SfxItemSet _aItemSet, sal_Int32 nTempIndex) :
            nIndex(nTempIndex), aItemSet(std::move(_aItemSet)) {}
    };

}

void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEnginebool bApi )
{
    //  PutData calls PutCell or SetNormalString

    bool bRet = false;
    ScDocument& rDoc = rDocShell.GetDocument();
    ScEditAttrTester aTester( &rEngine );
    bool bEditCell = aTester.NeedsObject();
    if ( bEditCell )
    {
        // #i61702# With bLoseContent set, the content of rEngine isn't restored
        // (used in loading XML, where after the removeActionLock call the API object's
        // EditEngine isn't accessed again.
        bool bLoseContent = rDoc.IsImportingXML();

        const bool bUpdateMode = rEngine.SetUpdateLayout(false);

        std::vector<std::unique_ptr<ScMyRememberItem>> aRememberItems;

        //  All paragraph attributes must be removed before calling CreateTextObject,
        //  not only alignment, so the object doesn't contain the cell attributes as
        //  paragraph attributes. Before removing the attributes store them in a vector to
        //  set them back to the EditEngine.
        sal_Int32 nCount = rEngine.GetParagraphCount();
        for (sal_Int32 i=0; i<nCount; i++)
        {
            const SfxItemSet& rOld = rEngine.GetParaAttribs( i );
            if ( rOld.Count() )
            {
                if ( !bLoseContent )
                {
                    aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i));
                }
                rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) );
            }
        }

        // A copy of pNewData will be stored in the cell.
        std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject());
        bRet = SetEditCell(rPos, *pNewData, !bApi);

        // Set the paragraph attributes back to the EditEngine.
        for (const auto& rxItem : aRememberItems)
        {
            rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet);
        }

        // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again
        if ( bUpdateMode && !bLoseContent )
            rEngine.SetUpdateLayout(true);
    }
    else
    {
        OUString aText = rEngine.GetText();
        if (aText.isEmpty())
        {
            bool bNumFmtSet = false;
            bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi );
        }
        else
            bRet = SetStringCell(rPos, aText, !bApi);
    }

    if ( !(bRet && aTester.NeedsCellAttr()) )
        return;

    const SfxItemSet& rEditAttr = aTester.GetAttribs();
    ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
    aPattern.GetFromEditItemSet( &rEditAttr );
    aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) );
    aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY );    // wasn't removed above if no edit object
    if ( aPattern.GetItemSet().Count() > 0 )
    {
        ScMarkData aMark(rDoc.GetSheetLimits());
        aMark.SelectTable( rPos.Tab(), true );
        aMark.SetMarkArea( ScRange( rPos ) );
        ApplyAttributes( aMark, aPattern, bApi );
    }
}

bool ScDocFunc::SetCellText(
    const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
    const formula::FormulaGrammar::Grammar eGrammar )
{
    bool bSet = false;
    if ( bInterpret )
    {
        if ( bEnglish )
        {
            ScDocument& rDoc = rDocShell.GetDocument();

            ::std::optional<ScExternalRefManager::ApiGuard> pExtRefGuard;
            if (bApi)
                pExtRefGuard.emplace(rDoc);

            ScInputStringType aRes =
                ScStringUtil::parseInputString(rDoc.GetNonThreadedContext(), rText, LANGUAGE_ENGLISH_US);

            switch (aRes.meType)
            {
                case ScInputStringType::Formula:
                    bSet = SetFormulaCell(rPos, new ScFormulaCell(rDoc, rPos, aRes.maText, eGrammar), !bApi);
                break;
                case ScInputStringType::Number:
                    bSet = SetValueCell(rPos, aRes.mfValue, !bApi);
                break;
                case ScInputStringType::Text:
                    bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi);
                break;
                default:
                    ;
            }
        }
        // otherwise keep Null -> SetString with local formulas/number formats
    }
    else if (!rText.isEmpty())
    {
        bSet = SetStringOrEditCell(rPos, rText, !bApi);
    }

    if (!bSet)
    {
        bool bNumFmtSet = false;
        bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi );
    }
    return bSet;
}

bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow )
{
    ScDocument& rDoc = rDocShell.GetDocument();
    ScPostIt* pNote = rDoc.GetNote( rPos );
    if( !pNote || (bShow == pNote->IsCaptionShown()) ||
        (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) )
        return false;

    // move the caption to internal or hidden layer and create undo action
    pNote->ShowCaption( rPos, bShow );
    if( rDoc.IsUndoEnabled() )
        rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) );

    rDoc.SetStreamValid(rPos.Tab(), false);

    ScTabView::OnLOKNoteStateChanged(pNote);

    if (ScViewData* pViewData = ScDocShell::GetViewData())
    {
        if (ScDrawView* pDrawView = pViewData->GetScDrawView())
            pDrawView->SyncForGrid( pNote->GetCaption());
    }

    rDocShell.SetDocumentModified();

    return true;
}

void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi )
{
    ScDocShellModificator aModificator( rDocShell );

    ScDocument& rDoc = rDocShell.GetDocument();
    ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
    if (!aTester.IsEditable())
    {
        if (!bApi)
            rDocShell.ErrorMessage(aTester.GetMessageId());
        return;
    }

    OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ???

    if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) )
        pNote->SetText( rPos, aNewText );

    //! Undo !!!

    rDoc.SetStreamValid(rPos.Tab(), false);

    rDocShell.PostPaintCell( rPos );
    aModificator.SetDocumentModified();
}

void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();
    ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
    if (aTester.IsEditable())
    {
        ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
        SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr;

        ScNoteData aOldData;
        std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
        sal_uInt32 nNoteId = 0;
        if( pOldNote )
        {
            nNoteId = pOldNote->GetId();
            // ensure existing caption object before draw undo tracking starts
            pOldNote->GetOrCreateCaption( rPos );
            // rescue note data for undo
            aOldData = pOldNote->GetNoteData();
        }

        // collect drawing undo actions for deleting/inserting caption objects
        if( pUndoMgr )
            pDrawLayer->BeginCalcUndo(false);

        // delete the note (creates drawing undo action for the caption object)
        bool hadOldNote(pOldNote);
        pOldNote.reset();

        // create new note (creates drawing undo action for the new caption object)
        ScNoteData aNewData;
        ScPostIt* pNewNote = nullptr;
        if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, falsetrue, nNoteId )) )
        {
            if( pAuthor ) pNewNote->SetAuthor( *pAuthor );
            if( pDate ) pNewNote->SetDate( *pDate );

            // rescue note data for undo
            aNewData = pNewNote->GetNoteData();
        }

        // create the undo action
        if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) )
            pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) );

        // repaint cell (to make note marker visible)
        rDocShell.PostPaintCell( rPos );

        rDoc.SetStreamValid(rPos.Tab(), false);

        aModificator.SetDocumentModified();

        // Let our LOK clients know about the new/modified note
        if (pNewNote)
        {
            ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add,
                                         rDoc, rPos, pNewNote);
        }
    }
    else if (!bApi)
    {
        rDocShell.ErrorMessage(aTester.GetMessageId());
    }
}

void ScDocFunc::ImportNote( const ScAddress& rPos,
                            std::unique_ptr<GenerateNoteCaption> xGenerator,
                            const tools::Rectangle& rCaptionRect, bool bShown )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();

    std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
    SAL_WARN_IF(pOldNote, "sc.ui""imported data has >1 notes on same cell? at pos " << rPos);

    // create new note
    ScNoteUtil::CreateNoteFromGenerator(rDoc, rPos, std::move(xGenerator),
                                        rCaptionRect, bShown);

    rDoc.SetStreamValid(rPos.Tab(), false);

    aModificator.SetDocumentModified();
}

bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
                                    bool bApi )
{
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bRecord = true;
    if ( !rDoc.IsUndoEnabled() )
        bRecord = false;

    bool bImportingXML = rDoc.IsImportingXML();
    bool bImportingXLSX = rDoc.IsImportingXLSX();
    // Cell formats can still be set if the range isn't editable only because of matrix formulas.
    // #i62483# When loading XML, the check can be skipped altogether.
    bool bOnlyNotBecauseOfMatrix;
    if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
            && !bOnlyNotBecauseOfMatrix )
    {
        if (!bApi)
            rDocShell.ErrorMessage(STR_PROTECTIONERR);
        return false;
    }

    ScDocShellModificator aModificator( rDocShell );

    //! Border

    ScRange aMultiRange;
    bool bMulti = rMark.IsMultiMarked();
    if ( bMulti )
        aMultiRange = rMark.GetMultiMarkArea();
    else
        aMultiRange = rMark.GetMarkArea();

    if ( bRecord )
    {
        ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO ));
        pUndoDoc->InitUndo( rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() );
        rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark);

        rDocShell.GetUndoManager()->AddUndoAction(
            std::make_unique<ScUndoSelectionAttr>(
                    rDocShell, rMark,
                    aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(),
                    aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(),
                    std::move(pUndoDoc), bMulti, &rPattern ) );
    }

    // While loading XML it is not necessary to ask HasAttrib. It needs too much time.
    sal_uInt16 nExtFlags = 0;
    if ( !bImportingXML && !bImportingXLSX )
        rDocShell.UpdatePaintExt( nExtFlags, aMultiRange );     // content before the change

    bool bChanged = false;
    rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged );

    if(bChanged)
    {
        if ( !bImportingXML && !bImportingXLSX )
            rDocShell.UpdatePaintExt( nExtFlags, aMultiRange );     // content after the change

        if (!AdjustRowHeight( aMultiRange, true, bApi ))
            rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags );
        else if (nExtFlags & SC_PF_LINES)
            lcl_PaintAbove( rDocShell, aMultiRange );   // because of lines above the range

        aModificator.SetDocumentModified();
    }

    return true;
}

bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
                                bool bApi )
{
    ScDocument& rDoc = rDocShell.GetDocument();
    bool bRecord = true;
    if ( !rDoc.IsUndoEnabled() )
        bRecord = false;

    bool bImportingXML = rDoc.IsImportingXML();
    // Cell formats can still be set if the range isn't editable only because of matrix formulas.
    // #i62483# When loading XML, the check can be skipped altogether.
    bool bOnlyNotBecauseOfMatrix;
    if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
            && !bOnlyNotBecauseOfMatrix )
    {
        if (!bApi)
            rDocShell.ErrorMessage(STR_PROTECTIONERR);
        return false;
    }

    ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find(
                                                rStyleName, SfxStyleFamily::Para ));
    if (!pStyleSheet)
        return false;

    ScDocShellModificator aModificator( rDocShell );

    ScRange aMultiRange;
    bool bMulti = rMark.IsMultiMarked();
    if ( bMulti )
        aMultiRange = rMark.GetMultiMarkArea();
    else
        aMultiRange = rMark.GetMarkArea();

    if ( bRecord )
    {
        ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
        SCTAB nStartTab = aMultiRange.aStart.Tab();
        SCTAB nTabCount = rDoc.GetTableCount();
        pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
        for (const auto& rTab : rMark)
        {
            if (rTab >= nTabCount)
                break;

            if (rTab != nStartTab)
                pUndoDoc->AddUndoTab( rTab, rTab );
        }

        ScRange aCopyRange = aMultiRange;
        aCopyRange.aStart.SetTab(0);
        aCopyRange.aEnd.SetTab(nTabCount-1);
        rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark );

        rDocShell.GetUndoManager()->AddUndoAction(
            std::make_unique<ScUndoSelectionStyle>(
                    rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) );

    }

    rDoc.ApplySelectionStyle( *pStyleSheet, rMark );

    if (!AdjustRowHeight( aMultiRange, true, bApi ))
        rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid );

    aModificator.SetDocumentModified();

    return true;
}

namespace {

/**
 * Check if this insertion attempt would end up cutting one or more pivot
 * tables in half, which is not desirable.
 *
 * @return true if this insertion can be done safely without shearing any
 *         existing pivot tables, false otherwise.
 */

bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument& rDoc)
{
    if (!rDoc.HasPivotTable())
        // This document has no pivot tables.
        return true;

    const ScDPCollection* pDPs = rDoc.GetDPCollection();

    ScRange aRange(rRange); // local copy
    switch (eCmd)
    {
        case INS_INSROWS_BEFORE:
        {
            aRange.aStart.SetCol(0);
            aRange.aEnd.SetCol(rDoc.MaxCol());
            [[fallthrough]];
        }
        case INS_CELLSDOWN:
        {
            auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
                return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
            if (bIntersects)
                // This column range cuts through at least one pivot table.  Not good.
                return false;

            // Start row must be either at the top or above any pivot tables.
            if (aRange.aStart.Row() < 0)
                // I don't know how to handle this case.
                return false;

            if (aRange.aStart.Row() == 0)
                // First row is always allowed.
                return true;

            ScRange aTest(aRange);
            aTest.aStart.IncRow(-1); // Test one row up.
            aTest.aEnd.SetRow(aTest.aStart.Row());
            for (const auto& rTab : rMarkData)
            {
                aTest.aStart.SetTab(rTab);
                aTest.aEnd.SetTab(rTab);
                if (pDPs->HasTable(aTest))
                    return false;
            }
        }
        break;
        case INS_INSCOLS_BEFORE:
        {
            aRange.aStart.SetRow(0);
            aRange.aEnd.SetRow(rDoc.MaxRow());
            [[fallthrough]];
        }
        case INS_CELLSRIGHT:
        {
            auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
                return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
            if (bIntersects)
                // This column range cuts through at least one pivot table.  Not good.
                return false;

            // Start row must be either at the top or above any pivot tables.
            if (aRange.aStart.Col() < 0)
                // I don't know how to handle this case.
                return false;

            if (aRange.aStart.Col() == 0)
                // First row is always allowed.
                return true;

            ScRange aTest(aRange);
            aTest.aStart.IncCol(-1); // Test one column to the left.
            aTest.aEnd.SetCol(aTest.aStart.Col());
            for (const auto& rTab : rMarkData)
            {
                aTest.aStart.SetTab(rTab);
                aTest.aEnd.SetTab(rTab);
                if (pDPs->HasTable(aTest))
                    return false;
            }
        }
        break;
        default:
            ;
    }
    return true;
}

/**
 * Check if this deletion attempt would end up cutting one or more pivot
 * tables in half, which is not desirable.
 *
 * @return true if this deletion can be done safely without shearing any
 *         existing pivot tables, false otherwise.
 */

bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument& rDoc)
{
    if (!rDoc.HasPivotTable())
        // This document has no pivot tables.
        return true;

    const ScDPCollection* pDPs = rDoc.GetDPCollection();

    ScRange aRange(rRange); // local copy

    switch (eCmd)
    {
        case DelCellCmd::Rows:
        {
            aRange.aStart.SetCol(0);
            aRange.aEnd.SetCol(rDoc.MaxCol());
            [[fallthrough]];
        }
        case DelCellCmd::CellsUp:
        {
            auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
                return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
            if (bIntersects)
                // This column range cuts through at least one pivot table.  Not good.
                return false;

            ScRange aTest(aRange);
            for (const auto& rTab : rMarkData)
            {
                aTest.aStart.SetTab(rTab);
                aTest.aEnd.SetTab(rTab);
                if (pDPs->HasTable(aTest))
                    return false;
            }
        }
        break;
        case DelCellCmd::Cols:
        {
            aRange.aStart.SetRow(0);
            aRange.aEnd.SetRow(rDoc.MaxRow());
            [[fallthrough]];
        }
        case DelCellCmd::CellsLeft:
        {
            auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
                return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
            if (bIntersects)
                // This column range cuts through at least one pivot table.  Not good.
                return false;

            ScRange aTest(aRange);
            for (const auto& rTab : rMarkData)
            {
                aTest.aStart.SetTab(rTab);
                aTest.aEnd.SetTab(rTab);
                if (pDPs->HasTable(aTest))
                    return false;
            }
        }
        break;
        default:
            ;
    }
    return true;
}

}

bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd,
                             bool bRecord, bool bApi, bool bPartOfPaste, size_t nInsertCount )
{
    ScDocShellModificator aModificator( rDocShell );
    ScDocument& rDoc = rDocShell.GetDocument();

    if (rDocShell.GetDocument().GetChangeTrack() &&
        ((eCmd == INS_CELLSDOWN  && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
         (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
    {
        // We should not reach this via UI disabled slots.
        assert(bApi);
        SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift");
        return false;
    }

    ScRange aTargetRange( rRange );

    // If insertion is for full cols/rows and after the current
    // selection, then shift the range accordingly
    if ( eCmd == INS_INSROWS_AFTER )
    {
        ScRange aErrorRange( ScAddress::UNINITIALIZED );
        if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange, rDoc))
        {
            return false;
        }
    }
    if ( eCmd == INS_INSCOLS_AFTER )
    {
        ScRange aErrorRange( ScAddress::UNINITIALIZED );
        if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange, rDoc))
        {
            return false;
        }
    }

    SCCOL nStartCol = aTargetRange.aStart.Col();
    SCROW nStartRow = aTargetRange.aStart.Row();
    SCTAB nStartTab = aTargetRange.aStart.Tab();
    SCCOL nEndCol = aTargetRange.aEnd.Col() + nInsertCount;
    SCROW nEndRow = aTargetRange.aEnd.Row() + nInsertCount;
    SCTAB nEndTab = aTargetRange.aEnd.Tab();

    if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
    {
        OSL_FAIL("invalid row in InsertCells");
        return false;
    }

    SCTAB nTabCount = rDoc.GetTableCount();
    SCCOL nPaintStartCol = nStartCol;
    SCROW nPaintStartRow = nStartRow;
    SCCOL nPaintEndCol = nEndCol;
    SCROW nPaintEndRow = nEndRow;
    PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
    bool bSuccess;

    ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();  //preserve current cursor position
    SCCOL nCursorCol = 0;
    SCROW nCursorRow = 0;
    if( pViewSh )
    {
        nCursorCol = pViewSh->GetViewData().GetCurX();
        nCursorRow = pViewSh->GetViewData().GetCurY();
    }

    if (bRecord && !rDoc.IsUndoEnabled())
        bRecord = false;

    ScMarkData aMark(rDoc.GetSheetLimits());
    if (pTabMark)
        aMark = *pTabMark;
    else
    {
        SCTAB nCount = 0;
        for( SCTAB i=0; i<nTabCount; i++ )
        {
            if( !rDoc.IsScenario(i) )
            {
                nCount++;
                if( nCount == nEndTab+1 )
                {
                    aMark.SelectTable( i, true );
                    break;
                }
            }
        }
    }

    ScMarkData aFullMark( aMark );          // including scenario sheets
    for (const auto& rTab : aMark)
    {
        if (rTab >= nTabCount)
            break;

        for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
            aFullMark.SelectTable( j, true );
    }

    SCTAB nSelCount = aMark.GetSelectCount();

    // Adjust also related scenarios

    SCCOL nMergeTestStartCol = nStartCol;
    SCROW nMergeTestStartRow = nStartRow;
    SCCOL nMergeTestEndCol = nEndCol;
    SCROW nMergeTestEndRow = nEndRow;

    ScRange aExtendMergeRange( aTargetRange );

    if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) )
    {
        rDoc.ExtendMerge( aExtendMergeRange );
        rDoc.ExtendOverlapped( aExtendMergeRange );
        nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
        nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
        nPaintEndCol = nMergeTestEndCol;
        nPaintEndRow = nMergeTestEndRow;
    }

    if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER )
    {
        nMergeTestStartCol = 0;
        nMergeTestEndCol = rDoc.MaxCol();
    }
    if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
    {
        nMergeTestStartRow = 0;
        nMergeTestEndRow = rDoc.MaxRow();
    }
    if ( eCmd == INS_CELLSDOWN )
        nMergeTestEndRow = rDoc.MaxRow();
    if ( eCmd == INS_CELLSRIGHT )
        nMergeTestEndCol = rDoc.MaxCol();

    bool bNeedRefresh = false;

    SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? rDoc.MaxCol() : nMergeTestEndCol;
    SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow;

    ScEditableTester aTester;

    switch (eCmd)
    {
        case INS_INSCOLS_BEFORE:
            aTester = ScEditableTester(
                rDoc, sc::EditAction::InsertColumnsBefore, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark);
            break;
        case INS_INSCOLS_AFTER:
            aTester = ScEditableTester(
                rDoc, sc::EditAction::InsertColumnsAfter, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark);
            break;
        case INS_INSROWS_BEFORE:
            aTester = ScEditableTester(
                rDoc, sc::EditAction::InsertRowsBefore, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark);
            break;
        case INS_INSROWS_AFTER:
            aTester = ScEditableTester(
                rDoc, sc::EditAction::InsertRowsAfter, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark);
            break;
        default:
            aTester = ScEditableTester(
                rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark);
    }

    if (!aTester.IsEditable())
    {
        if (!bApi)
            rDocShell.ErrorMessage(aTester.GetMessageId());
        return false;
    }

    // Check if this insertion is allowed with respect to pivot table.
    if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, rDoc))
    {
        if (!bApi)
            rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
        return false;
    }

    weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );      // important due to TrackFormulas at UpdateReference

    ScDocumentUniquePtr pRefUndoDoc;
    std::unique_ptr<ScRefUndoData> pUndoData;
    if ( bRecord )
    {
        pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
        pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );

        // pRefUndoDoc is filled in InsertCol / InsertRow

        pUndoData.reset(new ScRefUndoData( rDoc ));

        rDoc.BeginDrawUndo();
    }

    // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
    // the patch comes from mloiseleur and maoyg
    bool bInsertMerge = false;
    std::vector<ScRange> qIncreaseRange;
    OUString aUndo = ScResId( STR_UNDO_INSERTCELLS );
    if (bRecord)
    {
        ViewShellId nViewShellId(-1);
        if (pViewSh)
            nViewShellId = pViewSh->GetViewShellId();
        rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
    }
    std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;

    for (const SCTAB i : aMark)
    {
        if (i >= nTabCount)
            break;

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

--> maximum size reached

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

Messung V0.5
C=94 H=96 G=94

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