Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sc/source/ui/view/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 117 kB image not shown  

Quelle  viewfunc.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * 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 <address.hxx>
#include <config_features.h>

#include <scitems.hxx>

#include <sfx2/app.hxx>
#include <svx/algitem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/langitem.hxx>
#include <editeng/justifyitem.hxx>
#include <o3tl/unit_conversion.hxx>
#include <sfx2/bindings.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <svl/zformat.hxx>
#include <vcl/weld.hxx>
#include <vcl/virdev.hxx>
#include <stdlib.h>
#include <unotools/charclass.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <osl/diagnose.h>

#include <viewfunc.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
#include <attrib.hxx>
#include <patattr.hxx>
#include <sc.hrc>
#include <undocell.hxx>
#include <undoblk.hxx>
#include <refundo.hxx>
#include <olinetab.hxx>
#include <rangenam.hxx>
#include <globstr.hrc>
#include <global.hxx>
#include <stlsheet.hxx>
#include <editutil.hxx>
#include <formulacell.hxx>
#include <scresid.hxx>
#include <inputhdl.hxx>
#include <scmod.hxx>
#include <inputopt.hxx>
#include <compiler.hxx>
#include <docfunc.hxx>
#include <appoptio.hxx>
#include <sizedev.hxx>
#include <editable.hxx>
#include <scui_def.hxx>
#include <funcdesc.hxx>
#include <docuno.hxx>
#include <cellsuno.hxx>
#include <tokenarray.hxx>
#include <rowheightcontext.hxx>
#include <comphelper/lok.hxx>
#include <conditio.hxx>
#include <columnspanset.hxx>
#include <stringutil.hxx>
#include <SparklineList.hxx>

#include <memory>

static void ShowFilteredRows(ScDocument& rDoc, SCTAB nTab, SCCOLROW nStartNo, SCCOLROW nEndNo,
                             bool bShow)
{
    SCROW nFirstRow = nStartNo;
    SCROW nLastRow = nStartNo;
    do
    {
        if (!rDoc.RowFiltered(nFirstRow, nTab, nullptr, &nLastRow))
            rDoc.ShowRows(nFirstRow, nLastRow < nEndNo ? nLastRow : nEndNo, nTab, bShow);
        nFirstRow = nLastRow + 1;
    } while (nFirstRow <= nEndNo);
}

static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell &rDocSh&nbsp;)
{
    if( pCondFmt )
    {
        const ScRangeList& rRanges = pCondFmt->GetRange();

        rDocSh.PostPaint( rRanges, PaintPartFlags::All );
    }
}

static void lcl_PostRepaintSparkLine(sc::SparklineList* pSparklineList, const ScRange&&nbsp;rRange,
                                     ScDocShell& rDocSh)
{
    if (pSparklineList)
    {
        for (auto& rSparkLineGroup : pSparklineList->getSparklineGroups())
        {
            for (auto& rSparkline : pSparklineList->getSparklinesFor(rSparkLineGroup))
            {
                if (rSparkline->getInputRange().Contains(rRange))
                {
                    rDocSh.PostPaint(
                        ScRange(rSparkline->getColumn(), rSparkline->getRow(), rRange.aStart.Tab()),
                        PaintPartFlags::All, SC_PF_TESTMERGE);
                }
            }
        }
    }
}

ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
    ScTabView( pParent, rDocSh, pViewShell ),
    bFormatValid( false )
{
}

ScViewFunc::~ScViewFunc()
{
}

namespace {

struct FormulaProcessingContext
{
    std::shared_ptr<ScAddress> aPos;
    std::shared_ptr<ScCompiler> aComp;
    std::shared_ptr<ScDocShellModificator> aModificator;
    std::shared_ptr<ScTokenArray> pArr;
    std::shared_ptr<ScTokenArray> pArrFirst;

    std::shared_ptr<EditTextObject> xTextObject;
    ScMarkData aMark;
    ScViewFunc& rViewFunc;

    OUString aCorrectedFormula;
    OUString aFormula;
    OUString aString;

    SCCOL nCol;
    SCROW nRow;
    SCTAB nTab;

    bool bMatrixExpand;
    bool bNumFmtChanged;
    bool bRecord;

    ScViewData& GetViewData() const
    {
        return rViewFunc.GetViewData();
    }

    ScDocFunc& GetDocFunc() const
    {
        return GetViewData().GetDocFunc();
    }

    ScDocument& GetDoc() const
    {
        return GetViewData().GetDocument();
    }
};

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);
}

}

void ScViewFunc::StartFormatArea()
{
    //  anything to do?
    if (!ScModule::get()->GetInputOptions().GetExtendFormat())
        return;

    //  start only with single cell (marked or cursor position)
    ScRange aMarkRange;
    bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
    if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
        bOk = false;

    if (bOk)
    {
        bFormatValid = true;
        aFormatSource = aMarkRange.aStart;
        aFormatArea = ScRange( aFormatSource );
    }
    else
        bFormatValid = false;       // discard old range
}

bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
{
    //  anything to do?
    if (!ScModule::get()->GetInputOptions().GetExtendFormat())
        return false;

    //  Test: treat input with numberformat (bAttrChanged) always as new Attribute
    //  (discard old Area ). If not wanted, discard if-statement
    if ( bAttrChanged )
    {
        StartFormatArea();
        return false;
    }

    //! Test if cell empty ???

    bool bFound = false;
    ScRange aNewRange = aFormatArea;
    if ( bFormatValid && nTab == aFormatSource.Tab() )
    {
        if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
        {
            //  within range?
            if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
            {
                bFound = true;          // do not change range
            }
            //  left ?
            if ( nCol+1 == aFormatArea.aStart.Col() )
            {
                bFound = true;
                aNewRange.aStart.SetCol( nCol );
            }
            //  right ?
            if ( nCol == aFormatArea.aEnd.Col()+1 )
            {
                bFound = true;
                aNewRange.aEnd.SetCol( nCol );
            }
        }
        if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
        {
            //  top ?
            if ( nRow+1 == aFormatArea.aStart.Row() )
            {
                bFound = true;
                aNewRange.aStart.SetRow( nRow );
            }
            //  bottom ?
            if ( nRow == aFormatArea.aEnd.Row()+1 )
            {
                bFound = true;
                aNewRange.aEnd.SetRow( nRow );
            }
        }
    }

    if (bFound)
        aFormatArea = aNewRange;    // extend
    else
        bFormatValid = false;       // outside of range -> break

    return bFound;
}

void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
                                   bool bAttrChanged )
{
    ScDocShell& rDocSh = GetViewData().GetDocShell();
    ScDocument& rDoc = rDocSh.GetDocument();

    const ScPatternAttr* pSource = rDoc.GetPattern(
                            aFormatSource.Col(), aFormatSource.Row(), nTab );
    if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
    {
        ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
        ScMarkData aMark(rDoc.GetSheetLimits());
        aMark.SetMarkArea( aRange );

        ScDocFunc &rFunc = GetViewData().GetDocFunc();

        // pOldPattern is only valid until call to ApplyAttributes!
        const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
        const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
        if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
            rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );

        rFunc.ApplyAttributes( aMark, *pSource, false );
    }

    if ( bAttrChanged )                             // value entered with number format?
        aFormatSource.Set( nCol, nRow, nTab );      // then set a new source
}

//      additional routines

void ScViewData::setupSizeDeviceProviderForColWidth(const ScSizeDeviceProvider& ;rProv, Fraction& rZoomX, Fraction& rZoomY, double& rPPTX, double &rPPTY)
{
    if (rProv.IsPrinter())
    {
        rPPTX = rProv.GetPPTX();
        rPPTY = rProv.GetPPTY();
        rZoomX = rZoomY = Fraction(1, 1);
    }
    else
    {
        rPPTX = GetPPTX();
        rPPTY = GetPPTY();
        rZoomX = GetZoomX();
        rZoomY = GetZoomY();
    }
}

sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
{
    ScDocShell& rDocSh = GetViewData().GetDocShell();
    ScDocument& rDoc = rDocSh.GetDocument();
    ScMarkData& rMark = GetViewData().GetMarkData();

    ScSizeDeviceProvider aProv(rDocSh);

    Fraction aZoomX, aZoomY;
    double nPPTX, nPPTY;
    GetViewData().setupSizeDeviceProviderForColWidth(aProv, aZoomX, aZoomY, nPPTX, nPPTY);

    sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
                                nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
    return nTwips;
}

bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
{
    bool bRet;
    ScDocument& rDoc = GetViewData().GetDocument();
    ScMarkData& rMark = GetViewData().GetMarkData();
    if (rMark.IsMarked() || rMark.IsMultiMarked())
        bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
    else
    {
        SCCOL nCol = GetViewData().GetCurX();
        SCROW nRow = GetViewData().GetCurY();
        SCTAB nTab = GetViewData().GetTabNo();
        bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
            pOnlyNotBecauseOfMatrix );
    }
    return bRet;
}

static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
{
    const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
    if ( pFuncList )
    {
        sal_uLong nCount = pFuncList->GetCount();
        for (sal_uLong i=0; i<nCount; i++)
            if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
                return true;
    }
    return false;
}

static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
{
    sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
    sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
    sal_uInt16 nPos;
    for (nPos=0; nPos<nOldCount; nPos++)
        if (pOldList[nPos] == nOpCode)          // is the function already in the list?
        {
            if ( nPos == 0 )
                return false;                   // already at the top -> no change

            //  count doesn't change, so the original array is modified

            for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
                pOldList[nCopy] = pOldList[nCopy-1];
            pOldList[0] = nOpCode;

            return true;                        // list has changed
        }

    if ( !lcl_FunctionKnown( nOpCode ) )
        return false;                           // not in function list -> no change

    sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
    sal_uInt16 nNewList[LRU_MAX];
    nNewList[0] = nOpCode;
    for (nPos=1; nPos<nNewCount; nPos++)
        nNewList[nPos] = pOldList[nPos-1];
    rAppOpt.SetLRUFuncList( nNewList, nNewCount );

    return true;                                // list has changed
}

namespace HelperNotifyChanges
{
    static void NotifyIfChangesListeners(const ScDocShell &rDocShell, const ScMarkData& rMark,
                                         SCCOL nCol, SCROW nRow, const OUString& rType = u"cell-change"_ustr)
    {
        ScModelObj* pModelObj = rDocShell.GetModel();

        ScRangeList aChangeRanges;
        for (const auto& rTab : rMark)
            aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );

        if (getMustPropagateChangesModel(pModelObj))
            Notify(*pModelObj, aChangeRanges, rType);
        else
        {
            Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
                    ? u"data-area-invalidate"_ustr : u"data-area-extend"_ustr);
        }
    }
}

namespace
{
    class AutoCorrectQuery : public weld::MessageDialogController
    {
    private:
        std::unique_ptr<weld::TextView> m_xError;
    public:
        AutoCorrectQuery(weld::Window* pParent, const OUString& rFormula)
            : weld::MessageDialogController(pParent, u"modules/scalc/ui/warnautocorrect.ui"_ustr, u"WarnAutoCorrect"_ustr, u"grid"_ustr)
            , m_xError(m_xBuilder->weld_text_view(u"error"_ustr))
        {
            m_xDialog->set_default_response(RET_YES);

            const int nMaxWidth = m_xError->get_approximate_digit_width() * 65;
            const int nMaxHeight = m_xError->get_height_rows(6);
            m_xError->set_size_request(nMaxWidth, nMaxHeight);

            m_xError->set_text(rFormula);
        }
    };
}
namespace
{
    void runAutoCorrectQueryAsync(const std::shared_ptr<FormulaProcessingContext>& context);

    void performAutoFormatAndUpdate(std::u16string_view rString, const ScMarkData& rMark, SCCOL nCol,
                                    SCROW nRow, SCTAB nTab, bool bNumFmtChanged, bool bRecord,
                                    const std::shared_ptr<ScDocShellModificator>& pModificator,
                                    ScViewFunc& rViewFunc)
    {
        bool bAutoFormat = rViewFunc.TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);

        if (bAutoFormat)
            rViewFunc.DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);

        ScViewData& rViewData = rViewFunc.GetViewData();
        ScDocShell& rDocSh = rViewData.GetDocShell();
        rDocSh.UpdateOle(rViewData);

        const OUString aType(rString.empty() ? u"delete-content" : u"cell-change");
        HelperNotifyChanges::NotifyIfChangesListeners(rDocSh, rMark, nCol, nRow, aType);

        if (bRecord)
        {
            ScDocFunc &rFunc = rViewData.GetDocFunc();
            rFunc.EndListAction();
        }

        pModificator->SetDocumentModified();
        ScDocument& rDoc = rViewData.GetDocument();
        lcl_PostRepaintCondFormat(rDoc.GetCondFormat(nCol, nRow, nTab), rDocSh);
        lcl_PostRepaintSparkLine(rDoc.GetSparklineList(nTab), ScRange(nCol, nRow, nTab), rDocSh);
    }

    void finalizeFormulaProcessing(const std::shared_ptr<FormulaProcessingContext>& context)
    {
        // to be used in multiple tabs, the formula must be compiled anew
        // via ScFormulaCell copy-ctor because of RangeNames,
        // the same code-array for all cells is not possible.
        // If the array has an error, (it) must be RPN-erased in the newly generated
        // cells and the error be set explicitly, so that
        // via FormulaCell copy-ctor and Interpreter it will be, when possible,
        // ironed out again, too intelligent... e.g.: =1))
        FormulaError nError = context->pArr->GetCodeError();
        if ( nError == FormulaError::NONE )
        {
            //  update list of recent functions with all functions that
            //  are not within parentheses

            ScModule* pScMod = ScModule::get();
            ScAppOptions aAppOpt = pScMod->GetAppOptions();
            bool bOptChanged = false;

            formula::FormulaToken** ppToken = context->pArr->GetArray();
            sal_uInt16 nTokens = context->pArr->GetLen();
            sal_uInt16 nLevel = 0;
            for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
            {
                formula::FormulaToken* pTok = ppToken[nTP];
                OpCode eOp = pTok->GetOpCode();
                if ( eOp == ocOpen )
                    ++nLevel;
                else if ( eOp == ocClose && nLevel )
                    --nLevel;
                if ( nLevel == 0 && pTok->IsFunction() &&
                        lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
                    bOptChanged = true;
            }

            if ( bOptChanged )
            {
                pScMod->SetAppOptions(aAppOpt);
            }

            if (context->bMatrixExpand)
            {
                // If the outer function/operator returns an array/matrix then
                // enter a matrix formula. ScViewFunc::EnterMatrix() takes care
                // of selection/mark of the result dimensions or preselected
                // mark. If the user wanted less or a single cell then should
                // mark such prior to entering the formula.
                const formula::FormulaToken* pToken = context->pArr->LastRPNToken();
                if (pToken && (formula::FormulaCompiler::IsMatrixFunction( pToken->GetOpCode())
                            || pToken->IsInForceArray()))
                {
                    // Discard this (still empty here) Undo action,
                    // EnterMatrix() will create its own.
                    if (context->bRecord)
                        context->GetDocFunc().EndListAction();

                    // Use corrected formula string.
                    context->rViewFunc.EnterMatrix( context->aFormula, context->GetDoc().GetGrammar());

                    return;
                }
            }
        }

        ScFormulaCell aCell(context->GetDoc(), *context->aPos, std::move(*context->pArr), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);

        SCTAB i;
        SvNumberFormatter* pFormatter = context->GetDoc().GetFormatTable();
        for (const auto& rTab : context->aMark)
        {
            i = rTab;
            context->aPos->SetTab( i );
            const sal_uInt32 nIndex = context->GetDoc().GetAttr(
                        context->nCol, context->nRow, i, ATTR_VALUE_FORMAT )->GetValue();
            const SvNumFormatType nType = pFormatter->GetType( nIndex);
            if (nType == SvNumFormatType::TEXT ||
                    ((context->aString[0] == '+' || context->aString[0] == '-') && nError != FormulaError::NONE && context->aString == context->aFormula))
            {
                if ( context->xTextObject )
                {
                    // A clone of context->xTextObject will be stored in the cell.
                    context->GetDocFunc().SetEditCell(*(context->aPos), *context->xTextObject, true);
                }
                else
                    context->GetDocFunc().SetStringCell(*(context->aPos), context->aFormula, true);
            }
            else
            {
                ScFormulaCell* pCell = new ScFormulaCell( aCell, context->GetDoc(), *(context->aPos) );
                if ( nError != FormulaError::NONE )
                {
                    pCell->GetCode()->DelRPN();
                    pCell->SetErrCode( nError );
                    if(pCell->GetCode()->IsHyperLink())
                        pCell->GetCode()->SetHyperLink(false);
                }
                if (nType == SvNumFormatType::LOGICAL)
                {
                    // Reset to General so the actual format can be determined
                    // after the cell has been interpreted. A sticky boolean
                    // number format is highly likely unwanted... see tdf#75650.
                    // General of same locale as current number format.
                    const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
                    const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
                    const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
                    ScPatternAttr aPattern(context->GetDoc().getCellAttributeHelper());
                    aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
                    ScMarkData aMark(context->GetDoc().GetSheetLimits());
                    aMark.SelectTable( i, true);
                    aMark.SetMarkArea( ScRange( *(context->aPos)));
                    context->GetDocFunc().ApplyAttributes( aMark, aPattern, false);
                    context->bNumFmtChanged = true;
                }
                context->GetDocFunc().SetFormulaCell(*(context->aPos), pCell, true);
            }
        }

        performAutoFormatAndUpdate(context->aString, context->aMark, context->nCol,
                                   context->nRow, context->nTab, context->bNumFmtChanged,
                                   context->bRecord, context->aModificator, context->rViewFunc);
    }

    void parseAndCorrectFormula(std::shared_ptr<FormulaProcessingContext> context)
    {
        bool bAddEqual = false;
        context->pArr = context->aComp->CompileString(context->aFormula);
        bool bCorrected = context->aComp->IsCorrected();

        if (bCorrected) {
            context->pArrFirst = context->pArr;
            context->pArr = context->aComp->CompileString(context->aComp->GetCorrectedFormula());
        }

        if (context->pArr->GetCodeError() == FormulaError::NONE) {
            bAddEqual = true;
            context->aComp->CompileTokenArray();
            bCorrected |= context->aComp->IsCorrected();
        }

        if (bCorrected) {
            context->aCorrectedFormula = bAddEqual ? "=" + context->aComp->GetCorrectedFormula()
                                                   : context->aComp->GetCorrectedFormula();
            if (context->aCorrectedFormula.getLength() == 1) {
                // empty formula, just '='
                if (context->pArrFirst)
                    context->pArr = context->pArrFirst;
            }
            else
            {
                runAutoCorrectQueryAsync(context);
                return;
            }
        }
        finalizeFormulaProcessing(context);
    }

    void runAutoCorrectQueryAsync(const std::shared_ptr<FormulaProcessingContext>& context)
    {
        auto aQueryBox = std::make_shared<AutoCorrectQuery>(context->GetViewData().GetDialogParent(), context->aCorrectedFormula);
        weld::DialogController::runAsync(aQueryBox, [context] (int nResult)
        {
            if (nResult == RET_YES) {
                context->aFormula = context->aCorrectedFormula;
                parseAndCorrectFormula(context);
            } else {
                if (context->pArrFirst)
                    context->pArr = context->pArrFirst;

                finalizeFormulaProcessing(context);
            }
        });
    }
}

//      actual functions

//  input - undo OK
void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
                            const OUString& rString,
                            const EditTextObject* pData,
                            bool bMatrixExpand )
{
    ScDocument& rDoc = GetViewData().GetDocument();
    ScMarkData aMark(GetViewData().GetMarkData());
    bool bRecord = rDoc.IsUndoEnabled();
    SCTAB i;

    ScDocShell& rDocSh = GetViewData().GetDocShell();
    ScDocFunc &rFunc = GetViewData().GetDocFunc();
    std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(rDocSh);

    ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, aMark );
    if (!aTester.IsEditable())
    {
        ErrorMessage(aTester.GetMessageId());
        PaintArea(nCol, nRow, nCol, nRow);        // possibly the edit-engine is still painted there
        return;
    }

    if ( bRecord )
        rFunc.EnterListAction( STR_UNDO_ENTERDATA );

    bool bFormula = false;

    // do not check formula if it is a text cell
    sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab );
    SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
    // a single '=' character is handled as string (needed for special filters)
    if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 )
    {
        if ( rString[0] == '=' )
        {
            // handle as formula
            bFormula = true;
        }
        else if ( rString[0] == '+' || rString[0] == '-' )
        {
            // if there is more than one leading '+' or '-' character, remove the additional ones
            sal_Int32 nIndex = 1;
            sal_Int32 nLen = rString.getLength();
            while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
            {
                ++nIndex;
            }
            OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );

            // if the remaining part without the leading '+' or '-' character
            // is non-empty and not a number, handle as formula
            if ( aString.getLength() > 1 )
            {
                double fNumber = 0;
                if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) )
                {
                    bFormula = true;
                }
            }
        }
    }

    bool bNumFmtChanged = false;
    if ( bFormula )
    {   // formula, compile with autoCorrection
        i = aMark.GetFirstSelected();
        auto xPosPtr = std::make_shared<ScAddress>(nCol, nRow, i);
        auto xCompPtr = std::make_shared<ScCompiler>(rDoc, *xPosPtr, rDoc.GetGrammar(), truefalse);
        std::unique_ptr<EditTextObject> xTextObject(pData ? pData->Clone() : nullptr);

        //2do: enable/disable autoCorrection via calcoptions
        xCompPtr->SetAutoCorrection( true );
        if ( rString[0] == '+' || rString[0] == '-' )
        {
            xCompPtr->SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
        }

        OUString aFormula( rString );

        FormulaProcessingContext context_instance{
            std::move(xPosPtr), std::move(xCompPtr),    std::move(xModificator), nullptr,
            nullptr,            std::move(xTextObject), std::move(aMark),        *this,
            OUString(),         aFormula,               rString,                 nCol,
            nRow,               nTab,                   bMatrixExpand,           bNumFmtChanged,
            bRecord
        };

        parseAndCorrectFormula(std::make_shared<FormulaProcessingContext>(context_instance));
    }
    else
    {
        ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
        for (const auto& rTab : aMark)
        {
            bool bNumFmtSet = false;
            const ScAddress aScAddress(nCol, nRow, rTab);

            // tdf#104902 - handle embedded newline
            if (ScStringUtil::isMultiline(rString))
            {
                rEngine.SetTextCurrentDefaults(rString);
                rDoc.SetEditText(aScAddress, rEngine.CreateTextObject());
                rDocSh.AdjustRowHeight(nRow, nRow, rTab);
            }
            else
            {
                rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false);
            }

            if (bNumFmtSet)
            {
                /* FIXME: if set on any sheet results in changed only on
                 * sheet nTab for TestFormatArea() and DoAutoAttributes() */

                bNumFmtChanged = true;
            }
        }
        performAutoFormatAndUpdate(rString, aMark, nCol, nRow, nTab, bNumFmtChanged, bRecord, xModificator, *this);
    }
}

// enter value in single cell (on nTab only)

void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
{
    ScDocument& rDoc = GetViewData().GetDocument();
    ScDocShell& rDocSh = GetViewData().GetDocShell();

    bool bUndo(rDoc.IsUndoEnabled());
    ScDocShellModificator aModificator( rDocSh );

    ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
    if (aTester.IsEditable())
    {
        ScAddress aPos( nCol, nRow, nTab );
        ScCellValue aUndoCell;
        if (bUndo)
            aUndoCell.assign(rDoc, aPos);

        rDoc.SetValue( nCol, nRow, nTab, rValue );

        // because of ChangeTrack after change in document
        if (bUndo)
        {
            rDocSh.GetUndoManager()->AddUndoAction(
                std::make_unique<ScUndoEnterValue>(rDocSh, aPos, aUndoCell, rValue));
        }

        rDocSh.PostPaintCell( aPos );
        rDocSh.UpdateOle(GetViewData());
        aModificator.SetDocumentModified();
    }
    else
        ErrorMessage(aTester.GetMessageId());
}

void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
                            const EditTextObject& rData, bool bTestSimple )
{
    ScDocShell& rDocSh = GetViewData().GetDocShell();
    ScMarkData& rMark = GetViewData().GetMarkData();
    ScDocument& rDoc = rDocSh.GetDocument();
    bool bRecord = rDoc.IsUndoEnabled();

    ScDocShellModificator aModificator( rDocSh );

    ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
    if (aTester.IsEditable())
    {

        //      test for attribute

        bool bSimple = false;
        bool bCommon = false;
        std::unique_ptr<ScPatternAttr> pCellAttrs;
        OUString aString;

        const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
        ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), rDoc );
        aEngine.SetTextCurrentDefaults(rData);

        if (bTestSimple)                    // test, if simple string without attribute
        {
            ScEditAttrTester aAttrTester( &aEngine );
            bSimple = !aAttrTester.NeedsObject();
            bCommon = aAttrTester.NeedsCellAttr();

            // formulas have to be recognized even if they're formatted
            // (but common attributes are still collected)

            if (!bSimple)
            {
                OUString aParStr(aEngine.GetText( 0 ));
                if ( aParStr[0] == '=' )
                    bSimple = true;
            }

            if (bCommon)                // attribute for tab
            {
                pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
                pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
                //! remove common attributes from EditEngine?
            }
        }

        // #i97726# always get text for "repeat" of undo action
        aString = ScEditUtil::GetMultilineString(aEngine);

        //      undo

        std::unique_ptr<EditTextObject> pUndoData;
        ScUndoEnterData::ValuesType aOldValues;

        if (bRecord && !bSimple)
        {
            for (const auto& rTab : rMark)
            {
                ScUndoEnterData::Value aOldValue;
                aOldValue.mnTab = rTab;
                aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
                aOldValues.push_back(aOldValue);
            }

            pUndoData = rData.Clone();
        }

        //      enter data

        if (bCommon)
            rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs);         //! undo

        if (bSimple)
        {
            if (bCommon)
                AdjustRowHeight(nRow,nRow,true);

            EnterData( nCol, nRow, nTab, aString, nullptr, true /*bMatrixExpand*/);
        }
        else
        {
            for (const auto& rTab : rMark)
            {
                ScAddress aPos(nCol, nRow, rTab);
                rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
            }

            if ( bRecord )
            {   //  because of ChangeTrack current first
                rDocSh.GetUndoManager()->AddUndoAction(
                    std::make_unique<ScUndoEnterData>(rDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
            }

            HideAllCursors();

            AdjustRowHeight(nRow,nRow,true);

            for (const auto& rTab : rMark)
                rDocSh.PostPaintCell( nCol, nRow, rTab );

            ShowAllCursors();

            rDocSh.UpdateOle(GetViewData());

            bool bIsEmpty = rData.GetParagraphCount() == 0
                || (rData.GetParagraphCount() == 1 && !rData.HasText(0));
            const OUString aType(bIsEmpty ? u"delete-content" : u"cell-change");
            HelperNotifyChanges::NotifyIfChangesListeners(rDocSh, rMark, nCol, nRow, aType);

            aModificator.SetDocumentModified();
        }
        lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), rDocSh );
    }
    else
    {
        ErrorMessage(aTester.GetMessageId());
        PaintArea( nCol, nRow, nCol, nRow );        // possibly the edit-engine is still painted there
    }
}

void ScViewFunc::EnterDataAtCursor( const OUString& rString )
{
    SCCOL nPosX = GetViewData().GetCurX();
    SCROW nPosY = GetViewData().GetCurY();
    SCTAB nTab = GetViewData().GetTabNo();

    EnterData( nPosX, nPosY, nTab, rString );
    // tdf#154174: update repeated data in the cell
    GetViewData().GetViewShell()->UpdateInputHandler();
}

void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
{
    ScViewData& rData = GetViewData();
    const SCCOL nCol = rData.GetCurX();
    const SCROW nRow = rData.GetCurY();
    const ScMarkData& rMark = rData.GetMarkData();
    if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
    {
        //  nothing marked -> temporarily calculate block
        //  with size of result formula to get the size

        ScDocument& rDoc = rData.GetDocument();
        SCTAB nTab = rData.GetTabNo();
        ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );

        SCSIZE nSizeX;
        SCSIZE nSizeY;
        aFormCell.GetResultDimensions( nSizeX, nSizeY );
        if ( nSizeX != 0 && nSizeY != 0 &&
             nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
             nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
        {
            ScRange aResult( nCol, nRow, nTab,
                             sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
                             sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
            MarkRange( aResult, false );
        }
    }

    ScRange aRange;
    if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
    {
        ScDocShell& rDocSh = rData.GetDocShell();
        bool bSuccess = rDocSh.GetDocFunc().EnterMatrix(
            aRange, &rMark, nullptr, rString, falsefalse, OUString(), eGram );
        if (bSuccess)
            rDocSh.UpdateOle(GetViewData());
        else
            PaintArea(nCol, nRow, nCol, nRow);        // possibly the edit-engine is still painted there
    }
    else
        ErrorMessage(STR_NOMULTISELECT);
}

SvtScriptType ScViewFunc::GetSelectionScriptType()
{
    SvtScriptType nScript = SvtScriptType::NONE;

    ScDocument& rDoc = GetViewData().GetDocument();
    const ScMarkData& rMark = GetViewData().GetMarkData();
    if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
    {
        // no selection -> cursor

        nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
                            GetViewData().GetCurY(), GetViewData().GetTabNo());
    }
    else
    {
        ScRangeList aRanges;
        rMark.FillRangeListWithMarks( &aRanges, false );
        nScript = rDoc.GetRangeScriptType(aRanges);
    }

    if (nScript == SvtScriptType::NONE)
        nScript = ScGlobal::GetDefaultScriptType();

    return nScript;
}

static void ShrinkToDataArea(ScMarkData& rFuncMark, const ScDocument& rDoc);

const ScPatternAttr* ScViewFunc::GetSelectionPattern()
{
    // Don't use UnmarkFiltered in slot state functions, for performance reasons.
    // The displayed state is always that of the whole selection including filtered rows.

    ScMarkData aMark = GetViewData().GetMarkData();
    ScDocument& rDoc = GetViewData().GetDocument();

    // tdf#155368 if the selection is the whole sheet, we need to shrink the mark area, otherwise
    // we will not return a consistent result
    // (consistent compared to what happens in ScViewFunc::ApplySelectionPattern)
    ShrinkToDataArea( aMark, rDoc );

    if ( aMark.IsMarked() || aMark.IsMultiMarked() )
    {
        //  MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
        const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
        return pAttr;
    }
    else
    {
        SCCOL  nCol = GetViewData().GetCurX();
        SCROW  nRow = GetViewData().GetCurY();
        SCTAB  nTab = GetViewData().GetTabNo();

        // copy sheet selection
        aMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
        const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
        return pAttr;
    }
}

void ScViewFunc::GetSelectionFrame(
    std::shared_ptr<SvxBoxItem>& rLineOuter,
    std::shared_ptr<SvxBoxInfoItem>& rLineInner )
{
    ScDocument& rDoc = GetViewData().GetDocument();
    const ScMarkData& rMark = GetViewData().GetMarkData();

    if ( rMark.IsMarked() || rMark.IsMultiMarked() )
    {
        rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
    }
    else
    {
        const ScPatternAttr* pAttrs =
                    rDoc.GetPattern( GetViewData().GetCurX(),
                                      GetViewData().GetCurY(),
                                      GetViewData().GetTabNo() );

        rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
        rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());

        rLineInner->SetTable(false);
        rLineInner->SetDist(true);
        rLineInner->SetMinDist(false);
    }
}

//  apply attribute - undo OK
//
//  complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )

void ScViewFunc::ApplyAttributes( const SfxItemSet& rDialogSet,
                                  const SfxItemSet& rOldSet,
                                  bool bAdjustBlockHeight)
{
    // not editable because of matrix only? attribute OK nonetheless
    bool bOnlyNotBecauseOfMatrix;
    if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
    {
        ErrorMessage(STR_PROTECTIONERR);
        return;
    }

    ScDocument& rDoc = GetViewData().GetDocument();
    ScPatternAttr aOldAttrs(rDoc.getCellAttributeHelper(), &rOldSet);
    ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper(), &rDialogSet);
    aNewAttrs.DeleteUnchanged( &aOldAttrs );

    if ( rDialogSet.GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
    {   // don't reset to default SYSTEM GENERAL if not intended
        sal_uInt32 nOldFormat =
            rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
        sal_uInt32 nNewFormat =
            rDialogSet.Get( ATTR_VALUE_FORMAT ).GetValue();
        if ( nNewFormat != nOldFormat )
        {
            SvNumberFormatter* pFormatter =
                GetViewData().GetDocument().GetFormatTable();
            const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
            LanguageType eOldLang =
                pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
            const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
            LanguageType eNewLang =
                pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
            if ( eNewLang != eOldLang )
            {
                aNewAttrs.GetItemSet().Put(
                    SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );

                //  only the language has changed -> do not touch numberformat-attribute
                sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
                if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
                     nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
                    aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
            }
        }
    }

    if (rDialogSet.HasItem(ATTR_FONT_LANGUAGE))
        // font language has changed.  Redo the online spelling.
        ResetAutoSpell();

    const SvxBoxItem&     rOldOuter = rOldSet.Get(ATTR_BORDER);
    const SvxBoxItem&     rNewOuter = rDialogSet.Get(ATTR_BORDER);
    const SvxBoxInfoItem& rOldInner = rOldSet.Get(ATTR_BORDER_INNER);
    const SvxBoxInfoItem& rNewInner = rDialogSet.Get(ATTR_BORDER_INNER);
    SfxItemSet&           rNewSet   = aNewAttrs.GetItemSet();

    // protect referenced Items from disappearing (was: don't delete yet)
    const SfxPoolItemHolder aHoldOuter(*rDialogSet.GetPool() , &rNewOuter);
    const SfxPoolItemHolder aHoldInner(*rDialogSet.GetPool() , &rNewInner);
    (void)aHoldOuter;
    (void)aHoldInner;

    rNewSet.ClearItem( ATTR_BORDER );
    rNewSet.ClearItem( ATTR_BORDER_INNER );

    /*
     * establish whether border attribute is to be set:
     * 1. new != old
     * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
     *
     */


    bool bFrame =    (rDialogSet.GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
                  || (rDialogSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);

    if (SfxPoolItem::areSame(rNewOuter, rOldOuter) && SfxPoolItem::areSame(rNewInner, rOldInner))
        bFrame = false;

    //  this should be intercepted by the pool: ?!??!??

    if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
        bFrame = false;

    bFrame =   bFrame
            && (   rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
                || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
                || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
                || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
                || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
                || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );

    if (!bFrame)
        ApplySelectionPattern( aNewAttrs );            // standard only
    else
    {
        // if new items are default-items, overwrite the old items:

        bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
        bool bDefNewInner = IsStaticDefaultItem(&rNewInner);

        ApplyPatternLines( aNewAttrs,
                           bDefNewOuter ? rOldOuter : rNewOuter,
                           bDefNewInner ? &rOldInner : &rNewInner );
    }

    //  adjust height only if needed
    if (bAdjustBlockHeight)
        AdjustBlockHeight();

    // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
}

void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
{
    // not editable because of matrix only? attribute OK nonetheless
    bool bOnlyNotBecauseOfMatrix;
    if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
    {
        ErrorMessage(STR_PROTECTIONERR);
        return;
    }

    ScDocument& rDoc = GetViewData().GetDocument();
    ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());

    aNewAttrs.GetItemSet().Put( rAttrItem );
    //  if justify is set (with Buttons), always indentation 0
    if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
        aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
    ApplySelectionPattern( aNewAttrs );

    // Prevent useless compute
    if (bAdjustBlockHeight)
        AdjustBlockHeight();

    // CellContentChanged is called in ApplySelectionPattern
}

//  patterns and borders

void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem&&nbsp;rNewOuter,
                                    const SvxBoxInfoItem* pNewInner )
{
    ScDocument& rDoc = GetViewData().GetDocument();
    ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
    ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
    bool bRecord = true;
    if (!rDoc.IsUndoEnabled())
        bRecord = false;

    bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
    ScRange aMarkRange, aMarkRangeWithEnvelope;
    aFuncMark.MarkToSimple();
    bool bMulti = aFuncMark.IsMultiMarked();
    if (bMulti)
        aMarkRange = aFuncMark.GetMultiMarkArea();
    else if (aFuncMark.IsMarked())
        aMarkRange = aFuncMark.GetMarkArea();
    else
    {
        aMarkRange = ScRange( GetViewData().GetCurX(),
                            GetViewData().GetCurY(), GetViewData().GetTabNo() );
        DoneBlockMode();
        InitOwnBlockMode( aMarkRange );
        aFuncMark.SetMarkArea(aMarkRange);
        MarkDataChanged();
    }
    if( bRemoveAdjCellBorder )
        aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
    else
        aMarkRangeWithEnvelope = aMarkRange;

    ScDocShell& rDocSh = GetViewData().GetDocShell();

    ScDocShellModificator aModificator( rDocSh );

    if (bRecord)
    {
        ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
        SCTAB nStartTab = aMarkRange.aStart.Tab();
        SCTAB nTabCount = rDoc.GetTableCount();
        bool bCopyOnlyMarked = false;
        if( !bRemoveAdjCellBorder )
            bCopyOnlyMarked = bMulti;
        pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
        for (const auto& rTab : aFuncMark)
            if (rTab != nStartTab)
                pUndoDoc->AddUndoTab( rTab, rTab );

        ScRange aCopyRange = aMarkRangeWithEnvelope;
        aCopyRange.aStart.SetTab(0);
        aCopyRange.aEnd.SetTab(nTabCount-1);
        rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );

        rDocSh.GetUndoManager()->AddUndoAction(
            std::make_unique<ScUndoSelectionAttr>(
                rDocSh, aFuncMark,
                aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
                aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
                std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
    }

    sal_uInt16 nExt = SC_PF_TESTMERGE;
    rDocSh.UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change

    rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);

    rDocSh.UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change

    aFuncMark.MarkToMulti();
    rDoc.ApplySelectionPattern( rAttr, aFuncMark );

    rDocSh.PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
    rDocSh.UpdateOle(GetViewData());
    aModificator.SetDocumentModified();
    CellContentChanged();

    StartFormatArea();
}

// tdf#147842 if the marked area is the entire sheet, then shrink it to the data area.
// Otherwise ctrl-A, perform-action, will take a very long time as it tries to modify
// cells that we are not using.
static void ShrinkToDataArea(ScMarkData& rFuncMark, const ScDocument& rDoc)
{
    // do not make it marked if it is not already marked
    if (!rFuncMark.IsMarked())
        return;
    if (rFuncMark.IsMultiMarked())
        return;
    ScRange aMarkArea = rFuncMark.GetMarkArea();
    const ScSheetLimits& rLimits = rDoc.GetSheetLimits();
    if (aMarkArea.aStart.Row() != 0 || aMarkArea.aStart.Col() != 0)
        return;
    if (aMarkArea.aEnd.Row() != rLimits.MaxRow() || aMarkArea.aEnd.Col() != rLimits.MaxCol())
        return;
    if (aMarkArea.aStart.Tab() != aMarkArea.aEnd.Tab())
        return;
    SCCOL nStartCol = aMarkArea.aStart.Col();
    SCROW nStartRow = aMarkArea.aStart.Row();
    SCCOL nEndCol = aMarkArea.aEnd.Col();
    SCROW nEndRow = aMarkArea.aEnd.Row();
    rDoc.ShrinkToDataArea(aMarkArea.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow);
    aMarkArea.aStart.SetCol(nStartCol);
    aMarkArea.aStart.SetRow(nStartRow);
    aMarkArea.aEnd.SetCol(nEndCol);
    aMarkArea.aEnd.SetRow(nEndRow);
    rFuncMark.ResetMark();
    rFuncMark.SetMarkArea(aMarkArea);
}

//  pattern only

void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
{
    ScViewData& rViewData   = GetViewData();
    ScDocShell& rDocSh      = rViewData.GetDocShell();
    ScDocument& rDoc        = rDocSh.GetDocument();
    ScMarkData aFuncMark( rViewData.GetMarkData() );       // local copy for UnmarkFiltered
    ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );

    bool bRecord = true;
    if (!rDoc.IsUndoEnabled())
        bRecord = false;

    //  State from old ItemSet doesn't matter for paint flags, as any change will be
    //  from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
    //  New alignment is checked (check in PostPaint isn't enough) in case a right
    //  alignment is changed to left.
    const SfxItemSet& rNewSet = rAttr.GetItemSet();
    bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
                     rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
    bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;

    sal_uInt16 nExtFlags = 0;
    if ( bSetLines )
        nExtFlags |= SC_PF_LINES;
    if ( bSetAlign )
        nExtFlags |= SC_PF_WHOLEROWS;

    ScDocShellModificator aModificator( rDocSh );

    bool bMulti = aFuncMark.IsMultiMarked();
    aFuncMark.MarkToMulti();
    bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
    if (bOnlyTab)
    {
        SCCOL nCol = rViewData.GetCurX();
        SCROW nRow = rViewData.GetCurY();
        SCTAB nTab = rViewData.GetTabNo();
        aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
        aFuncMark.MarkToMulti();
    }

    ScRangeList aChangeRanges;

    if (aFuncMark.IsMultiMarked() && !bCursorOnly)
    {
        const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
        SCTAB nTabCount = rDoc.GetTableCount();
        for (const auto& rTab : aFuncMark)
        {
            ScRange aChangeRange( aMarkRange );
            aChangeRange.aStart.SetTab( rTab );
            aChangeRange.aEnd.SetTab( rTab );
            aChangeRanges.push_back( aChangeRange );
        }

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

        ScEditDataArray* pEditDataArray = nullptr;
        if (bRecord)
        {
            ScRange aCopyRange = aMarkRange;
            aCopyRange.aStart.SetTab(0);
            aCopyRange.aEnd.SetTab(nTabCount-1);

            ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
            pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
            for (const auto& rTab : aFuncMark)
                if (rTab != nStartTab)
                    pUndoDoc->AddUndoTab( rTab, rTab );
            rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );

            aFuncMark.MarkToMulti();

            ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
                rDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
                nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
            rDocSh.GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
            pEditDataArray = pUndoAttr->GetDataArray();
        }

        rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );

        rDocSh.PostPaint( nStartCol, nStartRow, nStartTab,
                           nEndCol,   nEndRow,   nEndTab,
                           PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
        rDocSh.UpdateOle(GetViewData());
        aModificator.SetDocumentModified();
        CellContentChanged();
    }
    else                            // single cell - simpler undo
    {
        SCCOL nCol = rViewData.GetCurX();
        SCROW nRow = rViewData.GetCurY();
        SCTAB nTab = rViewData.GetTabNo();

        std::unique_ptr<EditTextObject> pOldEditData;
        std::unique_ptr<EditTextObject> pNewEditData;
        ScAddress aPos(nCol, nRow, nTab);
        ScRefCellValue aCell(rDoc, aPos);
        if (aCell.getType() == CELLTYPE_EDIT)
        {
            const EditTextObject* pEditObj = aCell.getEditText();
            pOldEditData = pEditObj->Clone();
            rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
            pEditObj = rDoc.GetEditText(aPos);
            pNewEditData = pEditObj->Clone();
        }

        aChangeRanges.push_back(ScRange(aPos));
        std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));

        rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );

        const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );

        if (bRecord)
        {
            std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
                rDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
            pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
            rDocSh.GetUndoManager()->AddUndoAction(std::move(pUndo));
        }
        pOldPat.reset();     // is copied in undo (Pool)

        rDocSh.PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
        rDocSh.UpdateOle(GetViewData());
        aModificator.SetDocumentModified();
        CellContentChanged();
    }

    ScModelObj* pModelObj = rDocSh.GetModel();

    if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
    {
        css::uno::Sequence< css::beans::PropertyValue > aProperties;
        sal_Int32 nCount = 0;
        const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
        for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
        {
            const SfxPoolItem* pItem = nullptr;
            if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
            {
                for ( const auto pEntry : rMap.getPropertyEntries())
                {
                    if ( pEntry->nWID == nWhich )
                    {
                        css::uno::Any aVal;
                        pItem->QueryValue( aVal, pEntry->nMemberId );
                        aProperties.realloc( nCount + 1 );
                        auto pProperties = aProperties.getArray();
                        pProperties[ nCount ].Name = pEntry->aName;
                        pProperties[ nCount ].Value = std::move(aVal);
                        ++nCount;
                    }
                }
            }
        }
        HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"attribute"_ustr, aProperties);
    }

    StartFormatArea();
}

void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
{
    //  ItemSet from UI, may have different pool

    bool bOnlyNotBecauseOfMatrix;
    if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
    {
        ErrorMessage(STR_PROTECTIONERR);
        return;
    }

    ScPatternAttr aNewAttrs(GetViewData().GetDocument().getCellAttributeHelper());
    SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
    rNewSet.Put( rItemSet, false );
    ApplySelectionPattern( aNewAttrs );

    AdjustBlockHeight();
}

const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
{
    // Don't use UnmarkFiltered in slot state functions, for performance reasons.
    // The displayed state is always that of the whole selection including filtered rows.

    const ScStyleSheet* pSheet      = nullptr;
    ScViewData&         rViewData   = GetViewData();
    ScDocument&         rDoc        = rViewData.GetDocument();
    ScMarkData&         rMark       = rViewData.GetMarkData();

    if ( rMark.IsMarked() || rMark.IsMultiMarked() )
        pSheet = rDoc.GetSelectionStyle( rMark );                  // MarkToMulti isn't necessary
    else
        pSheet = rDoc.GetStyle( rViewData.GetCurX(),
                                rViewData.GetCurY(),
                                rViewData.GetTabNo() );

    return pSheet;
}

void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
{
    // not editable because of matrix only? attribute OK nonetheless
    bool bOnlyNotBecauseOfMatrix;
    if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
    {
        ErrorMessage(STR_PROTECTIONERR);
        return;
    }

    if ( !pStyleSheet) return;

    ScViewData& rViewData   = GetViewData();
    ScDocShell& rDocSh      = rViewData.GetDocShell();
    ScDocument& rDoc        = rDocSh.GetDocument();
    ScMarkData aFuncMark( rViewData.GetMarkData() );       // local copy for UnmarkFiltered
    ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
    SCTAB nTabCount     = rDoc.GetTableCount();
    bool bRecord = true;
    if (!rDoc.IsUndoEnabled())
        bRecord = false;

    ScDocShellModificator aModificator( rDocSh );

    if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
    {
        aFuncMark.MarkToMulti();
        const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();

        if ( bRecord )
        {
            SCTAB nTab = rViewData.GetTabNo();
            ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
            pUndoDoc->InitUndo( rDoc, nTab, nTab );
            for (const auto& rTab : aFuncMark)
                if (rTab != nTab)
                    pUndoDoc->AddUndoTab( rTab, rTab );

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

            OUString aName = pStyleSheet->GetName();
            rDocSh.GetUndoManager()->AddUndoAction(
                std::make_unique<ScUndoSelectionStyle>( rDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
        }

        rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );

        if (!AdjustBlockHeight())
            rViewData.GetDocShell().PostPaint( aMarkRange, PaintPartFlags::Grid );

        aFuncMark.MarkToSimple();
    }
    else
    {
        SCCOL nCol = rViewData.GetCurX();
        SCROW nRow = rViewData.GetCurY();
        SCTAB nTab = rViewData.GetTabNo();

        if ( bRecord )
        {
            ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
            pUndoDoc->InitUndo( rDoc, nTab, nTab );
            for (const auto& rTab : aFuncMark)
                if (rTab != nTab)
                    pUndoDoc->AddUndoTab( rTab, rTab );

            ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
            rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );

            ScRange aMarkRange ( nCol, nRow, nTab );
            ScMarkData aUndoMark = aFuncMark;
            aUndoMark.SetMultiMarkArea( aMarkRange );

            OUString aName = pStyleSheet->GetName();
            rDocSh.GetUndoManager()->AddUndoAction(
                std::make_unique<ScUndoSelectionStyle>( rDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
        }

        for (const auto& rTab : aFuncMark)
            rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );

        if (!AdjustBlockHeight())
            rViewData.GetDocShell().PostPaintCell( nCol, nRow, nTab );

    }

    aModificator.SetDocumentModified();

    StartFormatArea();
}

void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
{
    if ( !pStyleSheet) return;

    ScViewData& rViewData   = GetViewData();
    ScDocument& rDoc        = rViewData.GetDocument();
    ScDocShell& rDocSh      = rViewData.GetDocShell();

    ScDocShellModificator aModificator( rDocSh );

    ScopedVclPtrInstance< VirtualDevice > pVirtDev;
    pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
    rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
                                rViewData.GetPPTX(),
                                rViewData.GetPPTY(),
                                rViewData.GetZoomX(),
                                rViewData.GetZoomY() );

    rDocSh.PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
    aModificator.SetDocumentModified();

    ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
    if (pHdl)
        pHdl->ForgetLastPattern();
}

void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
{
    if ( !pStyleSheet) return;

    ScViewData& rViewData   = GetViewData();
    ScDocument& rDoc        = rViewData.GetDocument();
    ScDocShell& rDocSh      = rViewData.GetDocShell();

    ScDocShellModificator aModificator( rDocSh );

    ScopedVclPtrInstance< VirtualDevice > pVirtDev;
    pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
    rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
                                rViewData.GetPPTX(),
                                rViewData.GetPPTY(),
                                rViewData.GetZoomX(),
                                rViewData.GetZoomY() );

    rDocSh.PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
    aModificator.SetDocumentModified();

    ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
    if (pHdl)
        pHdl->ForgetLastPattern();
}


void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
{
    if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
        return;

    SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
    SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
    while (pViewShell)
    {
        ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
        if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
        {
            if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
                pPosHelper->invalidateByIndex(nStartCol);

            // if we remove a column the cursor position  and the current selection
            // in other views could need to be moved on the left by one column.
            if (pTabViewShell != this)
            {
                if (pTabViewShell->getPart() == nCurrentTabIndex)
                {
                    SCCOL nX = pTabViewShell->GetViewData().GetCurX();
                    if (nX > nStartCol)
                    {
                        tools::Long offset = nOffset;
                        if (nOffset + nStartCol > nX)
                            offset = nX - nStartCol;
                        else if (nOffset < 0 && nStartCol - nOffset > nX)
                            offset = -1 * (nX - nStartCol);

                        ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
                        SCROW nY = pTabViewShell->GetViewData().GetCurY();
                        pTabViewShell->SetCursor(nX + offset, nY);
                        if (pInputHdl && pInputHdl->IsInputMode())
                        {
                            pInputHdl->SetModified();
                        }
                    }

                    ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
                    aMultiMark.SetMarking( false );

                    if (aMultiMark.IsMultiMarked() || aMultiMark.IsMarked())
                    {
                        aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
                        pTabViewShell->SetMarkData(aMultiMark);
                    }
                }
                else
                {
                    SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
                    if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
                    {
                        pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
                    }
                }
            }
        }
        pViewShell = SfxViewShell::GetNext(*pViewShell);
    }
}

void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
{
    if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
        return;

    SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
    SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
    while (pViewShell)
    {
        ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
        if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
        {
            if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
                pPosHelper->invalidateByIndex(nStartRow);

            // if we remove a row the cursor position and the current selection
            // in other views could need to be moved up by one row.
            if (pTabViewShell != this)
            {
                if (pTabViewShell->getPart() == nCurrentTabIndex)
                {
                    SCROW nY = pTabViewShell->GetViewData().GetCurY();
                    if (nY > nStartRow)
--> --------------------

--> maximum size reached

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

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

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