/* -*- 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 <com/sun/star/table/BorderLineStyle.hpp>
#include <officecfg/Office/Calc.hxx>

#include <comphelper/lok.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/langitem.hxx>
#include <o3tl/temporary.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/request.hxx>
#include <sfx2/sfxdlg.hxx>
#include <sfx2/sidebar/Sidebar.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/newstyle.hxx>
#include <sfx2/tplpitem.hxx>
#include <svl/ilstitem.hxx>
#include <svl/numformat.hxx>
#include <svl/zformat.hxx>
#include <svl/int64item.hxx>
#include <svl/ptitem.hxx>
#include <svl/srchitem.hxx>
#include <svl/srchdefs.hxx>
#include <svl/stritem.hxx>
#include <svl/whiter.hxx>
#include <svx/numinf.hxx>
#include <svx/xbtmpit.hxx>
#include <svx/xlndsit.hxx>
#include <svx/xlnstit.hxx>
#include <svx/xlnedit.hxx>
#include <svx/xflgrit.hxx>
#include <svx/xflftrit.hxx>
#include <svx/xflhtit.hxx>
#include <svx/zoomslideritem.hxx>

#include <global.hxx>
#include <appoptio.hxx>
#include <attrib.hxx>
#include <cellform.hxx>
#include <cellvalue.hxx>
#include <compiler.hxx>
#include <docsh.hxx>
#include <document.hxx>
#include <formulacell.hxx>
#include <globstr.hrc>
#include <inputhdl.hxx>
#include <inputwin.hxx>
#include <markdata.hxx>
#include <patattr.hxx>
#include <sc.hrc>
#include <scabstdlg.hxx>
#include <scitems.hxx>
#include <scmod.hxx>
#include <scresid.hxx>
#include <stlpool.hxx>
#include <tabvwsh.hxx>
#include <tokenarray.hxx>
#include <viewdata.hxx>
#include <printfun.hxx>
#include <undostyl.hxx>
#include <futext.hxx>

#include <memory>

using namespace com::sun::star;

bool ScTabViewShell::GetFunction( OUString& rFuncStr, FormulaError nErrCode )
{
    sal_uInt32 nFuncs = ScModule::get()->GetAppOptions().GetStatusFunc();
    ScViewData& rViewData   = GetViewData();
    ScMarkData& rMark       = rViewData.GetMarkData();
    bool bIgnoreError = (rMark.IsMarked() || rMark.IsMultiMarked());
    bool bFirst = true;
    for ( sal_uInt16 nFunc = 0; nFunc < 32; nFunc++ )
    {
        if ( !(nFuncs & (1U << nFunc)) )
            continue;
        ScSubTotalFunc eFunc = static_cast<ScSubTotalFunc>(nFunc);

        if (bIgnoreError && (eFunc == SUBTOTAL_FUNC_CNT || eFunc == SUBTOTAL_FUNC_CNT2))
            nErrCode = FormulaError::NONE;

        if (nErrCode != FormulaError::NONE)
        {
            rFuncStr = ScGlobal::GetLongErrorString(nErrCode);
            return true;
        }

        TranslateId pGlobStrId;
        switch (eFunc)
        {
            case SUBTOTAL_FUNC_AVE:  pGlobStrId = STR_FUN_TEXT_AVG; break;
            case SUBTOTAL_FUNC_CNT:  pGlobStrId = STR_FUN_TEXT_COUNT; break;
            case SUBTOTAL_FUNC_CNT2: pGlobStrId = STR_FUN_TEXT_COUNT2; break;
            case SUBTOTAL_FUNC_MAX:  pGlobStrId = STR_FUN_TEXT_MAX; break;
            case SUBTOTAL_FUNC_MIN:  pGlobStrId = STR_FUN_TEXT_MIN; break;
            case SUBTOTAL_FUNC_SUM:  pGlobStrId = STR_FUN_TEXT_SUM; break;
            case SUBTOTAL_FUNC_SELECTION_COUNT: pGlobStrId = STR_FUN_TEXT_SELECTION_COUNT; break;

            default:
            {
                // added to avoid warnings
            }
        }
        if (pGlobStrId)
        {
            ScDocument& rDoc        = rViewData.GetDocument();
            SCCOL       nPosX       = rViewData.GetCurX();
            SCROW       nPosY       = rViewData.GetCurY();
            SCTAB       nTab        = rViewData.CurrentTabForData();

            OUString aStr = ScResId(pGlobStrId) + ": ";

            ScAddress aCursor( nPosX, nPosY, nTab );
            double nVal;
            if ( rDoc.GetSelectionFunction( eFunc, aCursor, rMark, nVal ) )
            {
                if ( nVal == 0.0 )
                    aStr += "0";
                else
                {
                    // Number in the standard format, the other on the cursor position
                    ScInterpreterContext& rContext = rDoc.GetNonThreadedContext();
                    sal_uInt32 nNumFmt = 0;
                    if ( eFunc != SUBTOTAL_FUNC_CNT && eFunc != SUBTOTAL_FUNC_CNT2 && eFunc != SUBTOTAL_FUNC_SELECTION_COUNT)
                    {
                        // number format from attributes or formula
                        nNumFmt = rDoc.GetNumberFormat( nPosX, nPosY, nTab );
                        // If the number format is time (without date) and the
                        // result is not within 24 hours, use a duration
                        // format. Summing date+time doesn't make much sense
                        // otherwise but we also don't want to display duration
                        // for a single date+time value.
                        if (nVal < 0.0 || nVal >= 1.0)
                        {
                            const SvNumberformat* pFormat = rContext.NFGetFormatEntry(nNumFmt);
                            if (pFormat && (pFormat->GetType() == SvNumFormatType::TIME))
                                nNumFmt = rContext.NFGetTimeFormat( nVal, pFormat->GetLanguage(), true);
                        }
                    }

                    OUString aValStr;
                    const Color* pDummy;
                    rContext.NFGetOutputString( nVal, nNumFmt, aValStr, &pDummy );
                    aStr += aValStr;
                }
            }
            if ( bFirst )
            {
                rFuncStr += aStr;
                bFirst = false;
            }
            else
                rFuncStr += "; " + aStr;
        }
    }

    return !rFuncStr.isEmpty();
}

//  Functions that are disabled, depending on the selection
//  Default:
//      SID_DELETE,
//      SID_DELETE_CONTENTS,
//      FID_DELETE_CELL
//      FID_VALIDATION

void ScTabViewShell::GetState( SfxItemSet& rSet )
{
    ScViewData& rViewData   = GetViewData();
    ScDocument& rDoc        = rViewData.GetDocument();
    ScDocShell* pDocShell   = rViewData.GetDocShell();
    ScMarkData& rMark       = rViewData.GetMarkData();
    SCCOL       nPosX       = rViewData.GetCurX();
    SCROW       nPosY       = rViewData.GetCurY();
    SCTAB       nTab        = rViewData.CurrentTabForData();

    SfxViewFrame& rThisFrame = GetViewFrame();
    bool bOle = GetViewFrame().GetFrame().IsInPlace();

    SCTAB nTabSelCount = rMark.GetSelectCount();

    SfxWhichIter    aIter(rSet);
    sal_uInt16          nWhich = aIter.FirstWhich();

    while ( nWhich )
    {
        switch ( nWhich )
        {
            case FID_CHG_COMMENT:
                {
                    ScDocShell* pDocSh = GetViewData().GetDocShell();
                    ScAddress aPos( nPosX, nPosY, nTab );
                    if ( pDocSh->IsReadOnly() || !pDocSh->GetChangeAction(aPos) || pDocSh->IsDocShared() )
                        rSet.DisableItem( nWhich );
                }
                break;

            case SID_OPENDLG_EDIT_PRINTAREA:
            case SID_ADD_PRINTAREA:
            case SID_DEFINE_PRINTAREA:
                {
                    if ( pDocShell && pDocShell->IsDocShared() )
                    {
                        rSet.DisableItem( nWhich );
                    }
                }
                break;

            case SID_DELETE_PRINTAREA:
                if ( pDocShell && pDocShell->IsDocShared() )
                {
                    rSet.DisableItem( nWhich );
                }
                else if (rDoc.IsPrintEntireSheet(nTab))
                    rSet.DisableItem(nWhich);
                break;

            case SID_STATUS_PAGESTYLE:
            case SID_HFEDIT:
                GetViewData().GetDocShell()->GetStatePageStyle( rSet, nTab );
                break;

            case SID_SEARCH_ITEM:
            {
                SvxSearchItem aItem(ScGlobal::GetSearchItem()); // make a copy.
                // Search on current selection if a range is marked.
                aItem.SetSelection(rMark.IsMarked());
                rSet.Put(aItem);
                break;
            }

            case SID_SEARCH_OPTIONS:
                {
                    // Anything goes
                    SearchOptionFlags nOptions = SearchOptionFlags::ALL;

                    // No replacement if ReadOnly
                    if (GetViewData().GetDocShell()->IsReadOnly() || IsCurrentLokViewReadOnly())
                        nOptions &= ~SearchOptionFlags( SearchOptionFlags::REPLACE | SearchOptionFlags::REPLACE_ALL );
                    rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(nOptions) ) );
                }
                break;

            case SID_CURRENTCELL:
                {
                    ScAddress aScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), 0 );
                    OUString  aAddr(aScAddress.Format(ScRefFlags::ADDR_ABS, nullptr, rDoc.GetAddressConvention()));
                    SfxStringItem   aPosItem( SID_CURRENTCELL, aAddr );

                    rSet.Put( aPosItem );
                }
                break;

            case SID_CURRENTTAB:
                // Table for Basic is 1-based
                rSet.Put( SfxUInt16Item( nWhich, static_cast<sal_uInt16>(GetViewData().CurrentTabForData()) + 1 ) );
                break;

            case SID_CURRENTDOC:
                rSet.Put( SfxStringItem( nWhich, GetViewData().GetDocShell()->GetTitle() ) );
                break;

            case FID_TOGGLEINPUTLINE:
                {
                    sal_uInt16 nId = ScInputWindowWrapper::GetChildWindowId();

                    if ( rThisFrame.KnowsChildWindow( nId ) )
                    {
                        SfxChildWindow* pWnd = rThisFrame.GetChildWindow( nId );
                        rSet.Put( SfxBoolItem( nWhich, pWnd != nullptr ) );
                    }
                    else
                        rSet.DisableItem( nWhich );
                }
                break;

            case FID_DEL_MANUALBREAKS:
                if (!rDoc.HasManualBreaks(nTab))
                    rSet.DisableItem( nWhich );
                break;

            case FID_RESET_PRINTZOOM:
                {
                    // disable if already set to default

                    OUString aStyleName = rDoc.GetPageStyle( nTab );
                    ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
                    SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName,
                                                    SfxStyleFamily::Page );
                    OSL_ENSURE( pStyleSheet, "PageStyle not found" );
                    if ( pStyleSheet )
                    {
                        SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
                        sal_uInt16 nScale = rStyleSet.Get(ATTR_PAGE_SCALE).GetValue();
                        sal_uInt16 nPages = rStyleSet.Get(ATTR_PAGE_SCALETOPAGES).GetValue();
                        if ( nScale == 100 && nPages == 0 )
                            rSet.DisableItem( nWhich );
                    }
                }
                break;

            case SID_ZOOM_IN:
                {
                    const Fraction& rZoomY = GetViewData().GetZoomY();
                    tools::Long nZoom = tools::Long(rZoomY * 100);
                    if (nZoom >= MAXZOOM)
                        rSet.DisableItem(nWhich);
                }
                break;
            case SID_ZOOM_OUT:
                {
                    const Fraction& rZoomY = GetViewData().GetZoomY();
                    tools::Long nZoom = tools::Long(rZoomY * 100);
                    if (nZoom <= MINZOOM)
                        rSet.DisableItem(nWhich);
                }
                break;

            case FID_SCALE:
            case SID_ATTR_ZOOM:
                if ( bOle )
                    rSet.DisableItem( nWhich );
                else
                {
                    const Fraction& rOldY = GetViewData().GetZoomY();
                    sal_uInt16 nZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));
                    rSet.Put( SvxZoomItem( SvxZoomType::PERCENT, nZoom, TypedWhichId<SvxZoomItem>(nWhich) ) );
                }
                break;

            case SID_ATTR_ZOOMSLIDER:
                {
                    if ( bOle )
                        rSet.DisableItem( nWhich );
                    else
                    {
                        const Fraction& rOldY = GetViewData().GetZoomY();
                        sal_uInt16 nCurrentZoom = static_cast<sal_uInt16>(tools::Long( rOldY * 100 ));

                        if( nCurrentZoom )
                        {
                            SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM, SID_ATTR_ZOOMSLIDER );
                            aZoomSliderItem.AddSnappingPoint( 100 );
                            rSet.Put( aZoomSliderItem );
                        }
                    }
                }
                break;

            case FID_FUNCTION_BOX:
            {
                const bool bBoxOpen = ::sfx2::sidebar::Sidebar::IsPanelVisible(u"ScFunctionsPanel",
                                                    rThisFrame.GetFrame().GetFrameInterface());
                rSet.Put(SfxBoolItem(nWhich, bBoxOpen));
                break;
            }

            case FID_TOGGLESYNTAX:
                rSet.Put(SfxBoolItem(nWhich, GetViewData().IsSyntaxMode()));
                break;

            case FID_TOGGLECOLROWHIGHLIGHTING:
                rSet.Put(SfxBoolItem(
                    nWhich,
                    officecfg::Office::Calc::Content::Display::ColumnRowHighlighting::get()));
                break;

            case FID_TOGGLEHEADERS:
                rSet.Put(SfxBoolItem(nWhich, GetViewData().IsHeaderMode()));
                break;

            case FID_TOGGLEFORMULA:
                {
                    const ScViewOptions& rOpts = rViewData.GetOptions();
                    bool bFormulaMode = rOpts.GetOption(sc::ViewOption::FORMULAS);
                    rSet.Put(SfxBoolItem(nWhich, bFormulaMode ));
                }
                break;

            case FID_NEW_SHEET_VIEW:
            case FID_REMOVE_SHEET_VIEW:
            case FID_SWITCH_TO_NEXT_SHEET_VIEW:
            case FID_SWITCH_TO_PREVIOUS_SHEET_VIEW:
            case FID_SELECT_SHEET_VIEW:
            case FID_EXIT_SHEET_VIEW:
                {
                    if (GetViewData().GetDocShell()->IsReadOnly() || IsCurrentLokViewReadOnly())
                    {
                        rSet.DisableItem(nWhich);
                    }
                    else if (nWhich == FID_REMOVE_SHEET_VIEW && GetViewData().GetSheetViewID() == sc::DefaultSheetViewID)
                    {
                        rSet.DisableItem(nWhich);
                    }
                }
                break;
            case FID_CURRENT_SHEET_VIEW:
                {
                    rSet.Put(SfxInt32Item(FID_CURRENT_SHEET_VIEW, GetViewData().GetSheetViewID()));
                }
                break;
            case FID_NORMALVIEWMODE:
            case FID_PAGEBREAKMODE:
                // always handle both slots - they exclude each other
                if ( bOle )
                {
                    rSet.DisableItem( FID_NORMALVIEWMODE );
                    rSet.DisableItem( FID_PAGEBREAKMODE );
                }
                else
                {
                    rSet.Put(SfxBoolItem(FID_NORMALVIEWMODE, !GetViewData().IsPagebreakMode()));
                    rSet.Put(SfxBoolItem(FID_PAGEBREAKMODE, GetViewData().IsPagebreakMode()));
                }
                break;

            case FID_PROTECT_DOC:
                {
                    if ( pDocShell && pDocShell->IsDocShared() )
                    {
                        rSet.DisableItem( nWhich );
                    }
                    else
                    {
                        rSet.Put( SfxBoolItem( nWhich, rDoc.IsDocProtected() ) );
                    }
                }
                break;

            case FID_PROTECT_TABLE:
                {
                    if ( pDocShell && pDocShell->IsDocShared() )
                    {
                        rSet.DisableItem( nWhich );
                    }
                    else
                    {
                        rSet.Put( SfxBoolItem( nWhich, rDoc.IsTabProtected( nTab ) ) );
                    }
                }
                break;

            case SID_AUTO_OUTLINE:
                {
                    if (rDoc.GetChangeTrack()!=nullptr || GetViewData().IsMultiMarked())
                    {
                        rSet.DisableItem( nWhich );
                    }
                }
                break;

            case SID_OUTLINE_DELETEALL:
                {
                    SCTAB nOlTab = GetViewData().CurrentTabForData();
                    ScOutlineTable* pOlTable = rDoc.GetOutlineTable( nOlTab );
                    if (pOlTable == nullptr)
                        rSet.DisableItem( nWhich );
                }
                break;

            case SID_WINDOW_SPLIT:
                rSet.Put(SfxBoolItem(nWhich,
                            rViewData.GetHSplitMode() == SC_SPLIT_NORMAL ||
                            rViewData.GetVSplitMode() == SC_SPLIT_NORMAL ));
                break;

            case SID_WINDOW_FIX:
                if(!comphelper::LibreOfficeKit::isActive())
                {
                    rSet.Put(SfxBoolItem(nWhich,
                                rViewData.GetHSplitMode() == SC_SPLIT_FIX ||
                                rViewData.GetVSplitMode() == SC_SPLIT_FIX ));
                }
                else
                {
                    rSet.Put(SfxBoolItem(nWhich,
                            rViewData.GetLOKSheetFreezeIndex(true) > 0 ||
                            rViewData.GetLOKSheetFreezeIndex(false) > 0 ));
                }
                break;

            case SID_WINDOW_FIX_COL:
            case SID_WINDOW_FIX_ROW:
                {
                    Point aPos;
                    bool bIsCol = (nWhich == SID_WINDOW_FIX_COL);
                    aPos.setX(rViewData.GetLOKSheetFreezeIndex(bIsCol));
                    aPos.setY(rViewData.CurrentTabForData());
                    rSet.Put(SfxPointItem(nWhich, aPos));
                }
                break;

            case FID_CHG_SHOW:
                {
                    if ( rDoc.GetChangeTrack() == nullptr || ( pDocShell && pDocShell->IsDocShared() ) )
                        rSet.DisableItem( nWhich );
                }
                break;
            case FID_CHG_ACCEPT:
                {
                    if(
                       ( !rDoc.GetChangeTrack() && !rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
                       ||
                       ( pDocShell && pDocShell->IsDocShared() )
                      )
                    {
                        rSet.DisableItem( nWhich);
                    }
                    else
                    {
                        rSet.Put(SfxBoolItem(FID_CHG_ACCEPT,
                            rThisFrame.HasChildWindow(FID_CHG_ACCEPT)));
                    }
                }
                break;

            case SID_FORMATPAGE:
                // in protected tables
                if ( pDocShell && ( pDocShell->IsReadOnly() || pDocShell->IsDocShared() ) )
                    rSet.DisableItem( nWhich );
                break;

            case SID_PRINTPREVIEW:
                // Toggle slot needs a State
                rSet.Put( SfxBoolItem( nWhich, false ) );
                break;

            case SID_READONLY_MODE:
                rSet.Put( SfxBoolItem( nWhich, GetViewData().GetDocShell()->IsReadOnly() ) );
                break;

            case FID_TAB_DESELECTALL:
                if ( nTabSelCount == 1 )
                    rSet.DisableItem( nWhich );     // enabled only if several sheets are selected
                break;

            case FID_TOGGLEHIDDENCOLROW:
                const svtools::ColorConfig& rColorCfg = ScModule::get()->GetColorConfig();
                rSet.Put( SfxBoolItem( nWhich, rColorCfg.GetColorValue(svtools::CALCHIDDENROWCOL).bIsVisible) );
                break;

        } // switch ( nWitch )
        nWhich = aIter.NextWhich();
    } // while ( nWitch )
}

void ScTabViewShell::ExecuteCellFormatDlg(SfxRequest& rReq, const OUString &rName)
{
    ScDocument& rDoc = GetViewData().GetDocument();

    std::shared_ptr<SvxBoxItem> aLineOuter(std::make_shared<SvxBoxItem>(ATTR_BORDER));
    std::shared_ptr<SvxBoxInfoItem> aLineInner(std::make_shared<SvxBoxInfoItem>(ATTR_BORDER_INNER));

    const ScPatternAttr*    pOldAttrs       = GetSelectionPattern();

    auto xOldSet = std::make_shared<SfxItemSet>(pOldAttrs->GetItemSet());

    xOldSet->MergeRange(XATTR_FILLSTYLE, XATTR_FILLCOLOR);

    xOldSet->MergeRange(SID_ATTR_BORDER_STYLES, SID_ATTR_BORDER_DEFAULT_WIDTH);

    // We only allow these border line types.
    std::vector<sal_Int32> aBorderStyles{
        table::BorderLineStyle::SOLID,
        table::BorderLineStyle::DOTTED,
        table::BorderLineStyle::DASHED,
        table::BorderLineStyle::FINE_DASHED,
        table::BorderLineStyle::DASH_DOT,
        table::BorderLineStyle::DASH_DOT_DOT,
        table::BorderLineStyle::DOUBLE_THIN };

    xOldSet->Put(SfxIntegerListItem(SID_ATTR_BORDER_STYLES, std::move(aBorderStyles)));

    // Set the default border width to 0.75 points.
    SfxInt64Item aBorderWidthItem(SID_ATTR_BORDER_DEFAULT_WIDTH, 75);
    xOldSet->Put(aBorderWidthItem);

    // Get border items and put them in the set:
    GetSelectionFrame( aLineOuter, aLineInner );

    //Fix border incorrect for RTL fdo#62399
    if( rDoc.IsLayoutRTL( GetViewData().CurrentTabForData() ) )
    {
        std::unique_ptr<SvxBoxItem> aNewFrame(aLineOuter->Clone());
        std::unique_ptr<SvxBoxInfoItem> aTempInfo(aLineInner->Clone());

        if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
            aNewFrame->SetLine( aLineOuter->GetLeft(), SvxBoxItemLine::RIGHT );
        if ( aLineInner->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
            aNewFrame->SetLine( aLineOuter->GetRight(), SvxBoxItemLine::LEFT );

        aLineInner->SetValid( SvxBoxInfoItemValidFlags::LEFT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT));
        aLineInner->SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT));

        xOldSet->Put( std::move(aNewFrame) );
    }
    else
    {
        xOldSet->Put( *aLineOuter );
    }

    xOldSet->Put( *aLineInner );

    // Generate NumberFormat Value from Value and Language and box it.
    if (pOldAttrs->HasValidNumberFormat()) // tdf#42989: don't set it for multi-format selection
        xOldSet->Put(
            SfxUInt32Item(ATTR_VALUE_FORMAT, pOldAttrs->GetNumberFormat(rDoc.GetFormatTable())));
    else // Make sure it's invalid, when ATTR_LANGUAGE_FORMAT is invalid
        xOldSet->InvalidateItem(ATTR_VALUE_FORMAT);

    std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem = MakeNumberInfoItem(rDoc, GetViewData());
    xOldSet->MergeRange( SID_ATTR_NUMBERFORMAT_INFO, SID_ATTR_NUMBERFORMAT_INFO );
    xOldSet->Put( std::move(pNumberInfoItem) );

    bInFormatDialog = true;
    ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();

    VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateScAttrDlg(GetFrameWeld(), xOldSet.get()));

    if (!rName.isEmpty())
        pDlg->SetCurPageId(rName);

    auto xRequest = std::make_shared<SfxRequest>(rReq);
    rReq.Ignore(); // the 'old' request is not relevant any more

    pDlg->StartExecuteAsync([pDlg, xOldSet=std::move(xOldSet), xRequest=std::move(xRequest), this](sal_Int32 nResult){
        bInFormatDialog = false;

        if ( nResult == RET_OK )
        {
            const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();
            if(const SvxNumberInfoItem* pItem = pOutSet->GetItemIfSet(SID_ATTR_NUMBERFORMAT_INFO))
            {
                UpdateNumberFormatter(*pItem);
            }

            ApplyAttributes(*pOutSet, *xOldSet);

            xRequest->Done(*pOutSet);
        }

        pDlg->disposeOnce();
    });
}

const OUString* ScTabViewShell::GetEditString() const
{
    if (mpInputHandler)
        return &mpInputHandler->GetEditString();

    return nullptr;
}

bool ScTabViewShell::IsRefInputMode() const
{
    if (ScModule* pScMod = ScModule::get())
    {
        if( pScMod->IsRefDialogOpen() )
            return pScMod->IsFormulaMode();
        if( pScMod->IsFormulaMode() )
        {
            ScInputHandler* pHdl = pScMod->GetInputHdl();
            if ( pHdl )
            {
                const ScViewData& rViewData = GetViewData();
                ScDocument& rDoc = rViewData.GetDocument();
                const ScAddress aPos( rViewData.GetCurPos() );
                const sal_uInt32 nIndex = rDoc.GetAttr(aPos, ATTR_VALUE_FORMAT ).GetValue();
                const SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
                if (nType == SvNumFormatType::TEXT)
                {
                    return false;
                }
                OUString aString = pHdl->GetEditString();
                if ( !pHdl->GetSelIsRef() && aString.getLength() > 1 &&
                     ( aString[0] == '+' || aString[0] == '-' ) )
                {
                    ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar() );
                    aComp.SetCloseBrackets( false );
                    std::unique_ptr<ScTokenArray> pArr(aComp.CompileString(aString));
                    if ( pArr && pArr->MayReferenceFollow() )
                    {
                        return true;
                    }
                }
                else
                {
                    return true;
                }
            }
        }
    }

    return false;
}

void ScTabViewShell::ExecuteInputDirect()
{
    if ( !IsRefInputMode() )
    {
        if (ScModule* pScMod = ScModule::get())
        {
            pScMod->InputEnterHandler();
        }
    }
}

void ScTabViewShell::UpdateInputHandler( bool bForce /* = sal_False */, bool bStopEditing /* = sal_True */ )
{
    ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : ScModule::get()->GetInputHdl();

    if ( pHdl )
    {
        OUString                aString;
        const EditTextObject*   pObject     = nullptr;
        ScViewData&             rViewData   = GetViewData();
        ScDocument&             rDoc        = rViewData.GetDocument();
        SCCOL                   nPosX       = rViewData.GetCurX();
        SCROW                   nPosY       = rViewData.GetCurY();
        SCTAB                   nTab        = rViewData.CurrentTabForData();
        SCTAB                   nStartTab   = 0;
        SCTAB                   nEndTab     = 0;
        SCCOL                   nStartCol   = 0;
        SCROW                   nStartRow   = 0;
        SCCOL                   nEndCol     = 0;
        SCROW                   nEndRow     = 0;
        ScAddress aPos = rViewData.GetCurPos();

        rViewData.GetSimpleArea( nStartCol, nStartRow, nStartTab,
                                  nEndCol,   nEndRow,   nEndTab );

        PutInOrder( nStartCol, nEndCol );
        PutInOrder( nStartRow, nEndRow );
        PutInOrder( nStartTab, nEndTab );

        bool bHideFormula = false;
        bool bHideAll     = false;

        if (rDoc.IsTabProtected(nTab))
        {
            const ScProtectionAttr& rProt = rDoc.GetAttr( nPosX,nPosY,nTab,
                                                           ATTR_PROTECTION);
            bHideFormula = rProt.GetHideFormula();
            bHideAll     = rProt.GetHideCell();
        }

        if (!bHideAll)
        {
            ScRefCellValue rCell(rDoc, aPos);
            if (rCell.getType() == CELLTYPE_FORMULA)
            {
                if (!bHideFormula)
                    aString = rCell.getFormula()->GetFormula();
            }
            else if (rCell.getType() == CELLTYPE_EDIT)
            {
                pObject = rCell.getEditText();
            }
            else
            {
                ScInterpreterContext& rContext = rDoc.GetNonThreadedContext();
                sal_uInt32 nNumFmt = rDoc.GetNumberFormat( ScRange(aPos) );

                aString = ScCellFormat::GetInputString( rCell, nNumFmt, &rContext, rDoc );
                if (rCell.getType() == CELLTYPE_STRING)
                {
                    // Put a ' in front if necessary, so that the string is not
                    // unintentionally interpreted as a number, and to show the
                    // user that it is a string (#35060#).
                    // If cell is not formatted as Text, a leading apostrophe
                    // needs another prepended, also '=' or '+' or '-'
                    // otherwise starting a formula.
                    // NOTE: this corresponds with
                    // sc/source/core/data/column3.cxx ScColumn::ParseString()
                    // removing one apostrophe.
                    // For number format Text IsNumberFormat() would never
                    // result in numeric anyway.
                    if (!rContext.NFIsTextFormat(nNumFmt) && (aString.startsWith("'")
                                || aString.startsWith("=") || aString.startsWith("+") || aString.startsWith("-")
                                || rContext.NFIsNumberFormat(aString, nNumFmt, o3tl::temporary(double()))))
                        aString = "'" + aString;
                }
            }
        }

        ScInputHdlState aState( ScAddress( nPosX,     nPosY,     nTab ),
                                ScAddress( nStartCol, nStartRow, nTab ),
                                ScAddress( nEndCol,   nEndRow,   nTab ),
                                aString,
                                pObject );

        //  if using the view's local input handler, this view can always be set
        //  as current view inside NotifyChange.
        ScTabViewShell* pSourceSh = mpInputHandler ? this : nullptr;

        pHdl->NotifyChange( &aState, bForce, pSourceSh, bStopEditing );
    }

    SfxBindings& rBindings = GetViewFrame().GetBindings();
    rBindings.Invalidate( SID_STATUS_SUM );         // always together with the input row
    rBindings.Invalidate( SID_ATTR_SIZE );
    rBindings.Invalidate( SID_TABLE_CELL );
}

void ScTabViewShell::UpdateInputHandlerCellAdjust( SvxCellHorJustify eJust )
{
    if( ScInputHandler* pHdl = mpInputHandler ? mpInputHandler.get() : ScModule::get()->GetInputHdl() )
        pHdl->UpdateCellAdjust( eJust );
}

void ScTabViewShell::ExecuteSave( SfxRequest& rReq )
{
    // only SID_SAVEDOC / SID_SAVEASDOC
    bool bCommitChanges = true;
    const SfxItemSet* pReqArgs = rReq.GetArgs();
    const SfxPoolItem* pItem = nullptr;

    bool bHasDontTerminateEdit = pReqArgs && pReqArgs->HasItem(FN_PARAM_1, &pItem);
    if (bHasDontTerminateEdit && pItem)
        bCommitChanges = !static_cast<const SfxBoolItem*>(pItem)->GetValue();

    // Finish entering unless 'DontTerminateEdit' is specified, even if a formula is being processed
    if (bCommitChanges)
    {
        bool bLOKActive = comphelper::LibreOfficeKit::isActive();

        // Disable error dialog box when about to save in lok mode as
        // this ultimately invokes SvpSalInstance::DoYield() when we want
        // to save immediately without committing any erroneous input in possibly
        // a cell with validation rules. After save is complete the user
        // can continue editing.
        ScModule::get()->InputEnterHandler(ScEnterMode::NORMAL, bLOKActive /* bBeforeSavingInLOK */);

        if (bLOKActive)
        {
            // Normally this isn't needed, but in Calc when editing a cell formula
            // and manually saving (without changing cells or hitting enter), while
            // InputEnterHandler will mark the doc as modified (when it is), because
            // we will save the doc immediately afterwards, the modified state event
            // is clobbered. To avoid that, we need to update SID_DOC_MODIFIED so that
            // a possible state of "true" after "InputEnterHandler" will be sent
            // as a notification. It is important that the notification goes through
            // normal process (cache) rather than directly notifying the views.
            // Otherwise, because there is a previous state of "false" in cache, the
            // "false" state after saving will be ignored.
            // This will work only if .uno:ModifiedStatus message will be removed from
            // the mechanism that keeps in the message queue only last message of
            // a particular status even if the values are different.
            if (SfxBindings* pBindings = GetViewData().GetDocShell()->GetViewBindings())
                pBindings->Update(SID_DOC_MODIFIED);
        }
    }

    if ( GetViewData().GetDocShell()->IsDocShared() )
    {
        GetViewData().GetDocShell()->SetDocumentModified();
    }

    // otherwise as normal
    GetViewData().GetDocShell()->ExecuteSlot( rReq );
}

void ScTabViewShell::GetSaveState( SfxItemSet& rSet )
{
    SfxShell* pDocSh = GetViewData().GetDocShell();

    SfxWhichIter aIter(rSet);
    sal_uInt16 nWhich = aIter.FirstWhich();
    while( nWhich )
    {
        if ( nWhich != SID_SAVEDOC || !GetViewData().GetDocShell()->IsDocShared() )
        {
            // get state from DocShell
            pDocSh->GetSlotState( nWhich, nullptr, &rSet );
        }
        nWhich = aIter.NextWhich();
    }
}

void ScTabViewShell::ExecDrawOpt( const SfxRequest& rReq )
{
    ScViewOptions aViewOptions = GetViewData().GetOptions();
    ScGridOptions aGridOptions = aViewOptions.GetGridOptions();

    SfxBindings& rBindings = GetViewFrame().GetBindings();
    const SfxItemSet* pArgs = rReq.GetArgs();
    const SfxPoolItem* pItem;
    sal_uInt16 nSlotId = rReq.GetSlot();
    switch (nSlotId)
    {
        case SID_GRID_VISIBLE:
            if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
            {
                aGridOptions.SetGridVisible( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
                aViewOptions.SetGridOptions(aGridOptions);
                rBindings.Invalidate(SID_GRID_VISIBLE);
            }
            break;

        case SID_GRID_USE:
            if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
            {
                aGridOptions.SetUseGridSnap( static_cast<const SfxBoolItem*>(pItem)->GetValue() );
                aViewOptions.SetGridOptions(aGridOptions);
                rBindings.Invalidate(SID_GRID_USE);
            }
            break;

        case SID_HELPLINES_MOVE:
            if ( pArgs && pArgs->GetItemState(nSlotId,true,&pItem) == SfxItemState::SET )
            {
                aViewOptions.SetOption(sc::ViewOption::HELPLINES, static_cast<const SfxBoolItem*>(pItem)->GetValue() );
                rBindings.Invalidate(SID_HELPLINES_MOVE);
            }
            break;
    }

    GetViewData().SetOptions(aViewOptions);
}

void ScTabViewShell::GetDrawOptState( SfxItemSet& rSet )
{
    SfxBoolItem aBool;

    const ScViewOptions& rViewOptions = GetViewData().GetOptions();
    const ScGridOptions& rGridOptions = rViewOptions.GetGridOptions();

    aBool.SetValue(rGridOptions.GetGridVisible());
    aBool.SetWhich( SID_GRID_VISIBLE );
    rSet.Put( aBool );

    aBool.SetValue(rGridOptions.GetUseGridSnap());
    aBool.SetWhich( SID_GRID_USE );
    rSet.Put( aBool );

    aBool.SetValue(rViewOptions.GetOption(sc::ViewOption::HELPLINES));
    aBool.SetWhich( SID_HELPLINES_MOVE );
    rSet.Put( aBool );
}

void ScTabViewShell::ExecStyle( SfxRequest& rReq )
{
    const SfxItemSet* pArgs = rReq.GetArgs();
    const sal_uInt16  nSlotId = rReq.GetSlot();
    if ( !pArgs && nSlotId != SID_STYLE_NEW_BY_EXAMPLE && nSlotId != SID_STYLE_UPDATE_BY_EXAMPLE )
    {
        // in case of vertical toolbar, sidebar shortcut icon
        GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
        return;
    }

    SfxBindings&        rBindings   = GetViewData().GetBindings();
    const SCTAB         nCurTab     = GetViewData().CurrentTabForData();
    ScDocShell*         pDocSh      = GetViewData().GetDocShell();
    ScDocument&         rDoc        = pDocSh->GetDocument();
    ScMarkData&         rMark       = GetViewData().GetMarkData();
    ScModule*           pScMod      = ScModule::get();
    SdrObject*          pEditObject = GetDrawView()->GetTextEditObject();
    OutlinerView*       pOLV        = GetDrawView()->GetTextEditOutlinerView();
    ESelection          aSelection  = pOLV ? pOLV->GetSelection() : ESelection();
    OUString            aRefName;
    bool                bUndo       = rDoc.IsUndoEnabled();

    SfxStyleSheetBasePool*  pStylePool  = rDoc.GetStyleSheetPool();
    SfxStyleSheetBase*      pStyleSheet = nullptr;

    bool bStyleToMarked = false;
    bool bListAction = false;
    bool bAddUndo = false;          // add ScUndoModifyStyle (style modified)
    auto xOldData = std::make_shared<ScStyleSaveData>();       // for undo/redo
    auto xNewData = std::make_shared<ScStyleSaveData>();

    SfxStyleFamily eFamily = SfxStyleFamily::Para;
    const SfxUInt16Item* pFamItem;
    const SfxStringItem* pFamilyNameItem;
    if ( pArgs && (pFamItem = pArgs->GetItemIfSet( SID_STYLE_FAMILY )) )
        eFamily = static_cast<SfxStyleFamily>(pFamItem->GetValue());
    else if ( pArgs && (pFamilyNameItem = pArgs->GetItemIfSet( SID_STYLE_FAMILYNAME )) )
    {
        const OUString& sFamily = pFamilyNameItem->GetValue();
        if (sFamily == "CellStyles")
            eFamily = SfxStyleFamily::Para;
        else if (sFamily == "PageStyles")
            eFamily = SfxStyleFamily::Page;
        else if (sFamily == "GraphicStyles")
            eFamily = SfxStyleFamily::Frame;
    }

    OUString                aStyleName;
    sal_uInt16              nRetMask = 0xffff;

    switch ( nSlotId )
    {
        case SID_STYLE_NEW:
            {
                const SfxPoolItem* pNameItem;
                if (pArgs && SfxItemState::SET == pArgs->GetItemState( nSlotId, true, &pNameItem ))
                    aStyleName  = static_cast<const SfxStringItem*>(pNameItem)->GetValue();

                const SfxStringItem* pRefItem=nullptr;
                if (pArgs && (pRefItem = pArgs->GetItemIfSet( SID_STYLE_REFERENCE )))
                {
                    aRefName  = pRefItem->GetValue();
                }

                pStyleSheet = &(pStylePool->Make( aStyleName, eFamily,
                                                  SfxStyleSearchBits::UserDefined ) );

                if (pStyleSheet->HasParentSupport())
                    pStyleSheet->SetParent(aRefName);
            }
            break;

        case SID_STYLE_APPLY:
        {
            const SfxStringItem* pNameItem = rReq.GetArg<SfxStringItem>(SID_APPLY_STYLE);
            const SfxStringItem* pFamilyItem = rReq.GetArg(SID_STYLE_FAMILYNAME);
            if ( pFamilyItem && pNameItem )
            {
                try
                {
                    css::uno::Reference< css::container::XNameAccess > xStyles;
                    css::uno::Reference< css::container::XNameAccess > xCont = pDocSh->GetModel()->getStyleFamilies();
                    xCont->getByName(pFamilyItem->GetValue()) >>= xStyles;
                    css::uno::Reference< css::beans::XPropertySet > xInfo;
                    xStyles->getByName( pNameItem->GetValue() ) >>= xInfo;
                    OUString aUIName;
                    xInfo->getPropertyValue(u"DisplayName"_ustr) >>= aUIName;
                    if ( !aUIName.isEmpty() )
                        rReq.AppendItem( SfxStringItem( SID_STYLE_APPLY, aUIName ) );
                }
                catch( css::uno::Exception& )
                {
                }
            }
            [[fallthrough]];
        }
        case SID_STYLE_EDIT:
        case SID_STYLE_DELETE:
        case SID_STYLE_HIDE:
        case SID_STYLE_SHOW:
        case SID_STYLE_NEW_BY_EXAMPLE:
            {
                const SfxPoolItem* pNameItem;
                if (pArgs && SfxItemState::SET == pArgs->GetItemState(nSlotId, true, &pNameItem))
                    aStyleName = static_cast<const SfxStringItem*>(pNameItem)->GetValue();
                else if ( nSlotId == SID_STYLE_NEW_BY_EXAMPLE )
                {
                    weld::Window* pDialogParent = rReq.GetFrameWeld();
                    if (!pDialogParent)
                        pDialogParent = GetFrameWeld();
                    SfxNewStyleDlg aDlg(pDialogParent, *pStylePool, eFamily);
                    if (aDlg.run() != RET_OK)
                        return;
                    aStyleName = aDlg.GetName();
                }

                pStyleSheet = pStylePool->Find( aStyleName, eFamily );

                xOldData->InitFromStyle( pStyleSheet );
            }
            break;

        case SID_STYLE_WATERCAN:
        {
            bool bWaterCan = pScMod->GetIsWaterCan();

            if( !bWaterCan )
            {
                const SfxPoolItem* pItem;

                if (pArgs && SfxItemState::SET == pArgs->GetItemState(nSlotId, true, &pItem))
                {
                    const SfxStringItem* pStrItem = dynamic_cast< const SfxStringItem *>( pItem );
                    if ( pStrItem )
                    {
                        aStyleName  = pStrItem->GetValue();
                        pStyleSheet = pStylePool->Find( aStyleName, eFamily );

                        if ( pStyleSheet )
                        {
                            static_cast<ScStyleSheetPool*>(pStylePool)->
                                    SetActualStyleSheet( pStyleSheet );
                            rReq.Done();
                        }
                    }
                }
            }

            if ( !bWaterCan && pStyleSheet )
            {
                pScMod->SetWaterCan( true );
                SetActivePointer( PointerStyle::Fill );
                rReq.Done();
            }
            else
            {
                pScMod->SetWaterCan( false );
                SetActivePointer( PointerStyle::Arrow );
                rReq.Done();
            }
        }
        break;

        default:
            break;
    }

    // set new style for paintbrush format mode
    if ( nSlotId == SID_STYLE_APPLY && pScMod->GetIsWaterCan() && pStyleSheet )
        static_cast<ScStyleSheetPool*>(pStylePool)->SetActualStyleSheet( pStyleSheet );

    switch ( eFamily )
    {
        case SfxStyleFamily::Para:
        {
            switch ( nSlotId )
            {
                case SID_STYLE_DELETE:
                {
                    if ( pStyleSheet )
                    {
                        RemoveStyleSheetInUse( pStyleSheet );
                        pStylePool->Remove( pStyleSheet );
                        InvalidateAttribs();
                        nRetMask = sal_uInt16(true);
                        bAddUndo = true;
                        rReq.Done();
                    }
                    else
                        nRetMask = sal_uInt16(false);
                }
                break;

                case SID_STYLE_HIDE:
                case SID_STYLE_SHOW:
                {
                    if ( pStyleSheet )
                    {
                        pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
                        InvalidateAttribs();
                        rReq.Done();
                    }
                    else
                        nRetMask = sal_uInt16(false);
                }
                break;

                case SID_STYLE_APPLY:
                {
                    if ( pStyleSheet && !pScMod->GetIsWaterCan() )
                    {
                        // apply style sheet to document
                        SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
                        InvalidateAttribs();
                        rReq.Done();
                    }
                }
                break;

                case SID_STYLE_NEW_BY_EXAMPLE:
                case SID_STYLE_UPDATE_BY_EXAMPLE:
                {
                    // create/replace style sheet by attributes
                    // at cursor position:

                    const ScPatternAttr* pAttrItem = nullptr;

                    // The query if marked, was always wrong here,
                    // so now no more, and just from the cursor.
                    // If attributes are to be removed from the selection, still need to be
                    // cautious not to adopt items from templates
                    // (GetSelectionPattern also collects items from originals) (# 44748 #)
                    SCCOL       nCol = GetViewData().GetCurX();
                    SCROW       nRow = GetViewData().GetCurY();
                    pAttrItem = rDoc.GetPattern( nCol, nRow, nCurTab );

                    SfxItemSet aAttrSet = pAttrItem->GetItemSet();
                    aAttrSet.ClearItem( ATTR_MERGE );
                    aAttrSet.ClearItem( ATTR_MERGE_FLAG );

                    // Do not adopt conditional formatting and validity,
                    // because they can not be edited in the template
                    aAttrSet.ClearItem( ATTR_VALIDDATA );
                    aAttrSet.ClearItem( ATTR_CONDITIONAL );

                    if ( SID_STYLE_NEW_BY_EXAMPLE == nSlotId )
                    {
                        if ( bUndo )
                        {
                            OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
                            pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
                            bListAction = true;
                        }

                        bool bConvertBack = false;
                        SfxStyleSheet*  pSheetInUse = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());

                        // when a new style is present and is used in the selection,
                        // then the parent can not be adopted:
                        if ( pStyleSheet && pSheetInUse && pStyleSheet == pSheetInUse )
                            pSheetInUse = nullptr;

                        // if already present, first remove ...
                        if ( pStyleSheet )
                        {
                            // style pointer to names before erase,
                            // otherwise cells will get invalid pointer
                            //!!! As it happens, a method that does it for a particular style
                            rDoc.getCellAttributeHelper().AllStylesToNames();
                            bConvertBack = true;
                            pStylePool->Remove(pStyleSheet);
                        }

                        // ...and create new
                        pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
                                                         SfxStyleSearchBits::UserDefined );

                        // when a style is present, then this will become
                        // the parent of the new style:
                        if ( pSheetInUse && pStyleSheet->HasParentSupport() )
                            pStyleSheet->SetParent( pSheetInUse->GetName() );

                        if ( bConvertBack )
                            // Name to style pointer
                            rDoc.getCellAttributeHelper().UpdateAllStyleSheets(rDoc);
                        else
                            rDoc.getCellAttributeHelper().CellStyleCreated(rDoc, aStyleName);

                        // Adopt attribute and use style
                        pStyleSheet->GetItemSet().Put( aAttrSet );
                        UpdateStyleSheetInUse( pStyleSheet );

                        //  call SetStyleSheetToMarked after adding the ScUndoModifyStyle
                        //  (pStyleSheet pointer is used!)
                        bStyleToMarked = true;
                    }
                    else // ( nSlotId == SID_STYLE_UPDATE_BY_EXAMPLE )
                    {
                        pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());

                        if ( pStyleSheet )
                        {
                            xOldData->InitFromStyle( pStyleSheet );

                            if ( bUndo )
                            {
                                OUString aUndo = ScResId( STR_UNDO_EDITCELLSTYLE );
                                pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
                                bListAction = true;
                            }

                            pStyleSheet->GetItemSet().Put( aAttrSet );
                            UpdateStyleSheetInUse( pStyleSheet );

                            //  call SetStyleSheetToMarked after adding the ScUndoModifyStyle
                            //  (pStyleSheet pointer is used!)
                            bStyleToMarked = true;
                        }
                    }

                    xNewData->InitFromStyle( pStyleSheet );
                    bAddUndo = true;
                    rReq.Done();
                }
                break;

                default:
                    break;
            }
        } // case SfxStyleFamily::Para:
        break;

        case SfxStyleFamily::Page:
        {
            switch ( nSlotId )
            {
                case SID_STYLE_DELETE:
                {
                    nRetMask = sal_uInt16( nullptr != pStyleSheet );
                    if ( pStyleSheet )
                    {
                        if ( rDoc.RemovePageStyleInUse( pStyleSheet->GetName() ) )
                        {
                            ScPrintFunc( *pDocSh, GetPrinter(true), nCurTab ).UpdatePages();
                            rBindings.Invalidate( SID_STATUS_PAGESTYLE );
                            rBindings.Invalidate( FID_RESET_PRINTZOOM );
                        }
                        pStylePool->Remove( pStyleSheet );
                        rBindings.Invalidate( SID_STYLE_FAMILY4 );
                        pDocSh->SetDocumentModified();
                        bAddUndo = true;
                        rReq.Done();
                    }
                }
                break;

                case SID_STYLE_HIDE:
                case SID_STYLE_SHOW:
                {
                    nRetMask = sal_uInt16( nullptr != pStyleSheet );
                    if ( pStyleSheet )
                    {
                        pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
                        rBindings.Invalidate( SID_STYLE_FAMILY4 );
                        pDocSh->SetDocumentModified();
                        rReq.Done();
                    }
                }
                break;

                case SID_STYLE_APPLY:
                {
                    nRetMask = sal_uInt16( nullptr != pStyleSheet );
                    if ( pStyleSheet && !pScMod->GetIsWaterCan() )
                    {
                        std::unique_ptr<ScUndoApplyPageStyle> pUndoAction;
                        SCTAB nTabCount = rDoc.GetTableCount();
                        for (const auto& rTab : rMark)
                        {
                            if (rTab >= nTabCount)
                                break;
                            OUString aOldName = rDoc.GetPageStyle( rTab );
                            if ( aOldName != aStyleName )
                            {
                                rDoc.SetPageStyle( rTab, aStyleName );
                                ScPrintFunc( *pDocSh, GetPrinter(true), rTab ).UpdatePages();
                                if( !pUndoAction )
                                    pUndoAction.reset(new ScUndoApplyPageStyle( *pDocSh, aStyleName ));
                                pUndoAction->AddSheetAction( rTab, aOldName );
                            }
                        }
                        if( pUndoAction )
                        {
                            pDocSh->GetUndoManager()->AddUndoAction( std::move(pUndoAction) );
                            pDocSh->SetDocumentModified();
                            rBindings.Invalidate( SID_STYLE_FAMILY4 );
                            rBindings.Invalidate( SID_STATUS_PAGESTYLE );
                            rBindings.Invalidate( FID_RESET_PRINTZOOM );
                        }
                        rReq.Done();
                    }
                }
                break;

                case SID_STYLE_NEW_BY_EXAMPLE:
                {
                    const OUString aStrCurStyle = rDoc.GetPageStyle( nCurTab );

                    if ( aStrCurStyle != aStyleName )
                    {
                        SfxStyleSheetBase*  pCurStyle = pStylePool->Find( aStrCurStyle, eFamily );
                        SfxItemSet          aAttrSet  = pCurStyle->GetItemSet();
                        SCTAB               nInTab;
                        bool                bUsed = rDoc.IsPageStyleInUse( aStyleName, &nInTab );

                        // if already present, first remove...
                        if ( pStyleSheet )
                            pStylePool->Remove( pStyleSheet );

                        // ...and create new
                        pStyleSheet = &pStylePool->Make( aStyleName, eFamily,
                                                         SfxStyleSearchBits::UserDefined );

                        // Adopt attribute
                        pStyleSheet->GetItemSet().Put( aAttrSet );
                        pDocSh->SetDocumentModified();

                        // If being used -> Update
                        if ( bUsed )
                            ScPrintFunc( *pDocSh, GetPrinter(true), nInTab ).UpdatePages();

                        xNewData->InitFromStyle( pStyleSheet );
                        bAddUndo = true;
                        rReq.Done();
                        nRetMask = sal_uInt16(true);
                    }
                }
                break;

                default:
                    break;
            } // switch ( nSlotId )
        } // case SfxStyleFamily::Page:
        break;

        case SfxStyleFamily::Frame:
        {
            switch ( nSlotId )
            {
                case SID_STYLE_DELETE:
                {
                    if ( pStyleSheet )
                    {
                        pStylePool->Remove( pStyleSheet );
                        InvalidateAttribs();
                        pDocSh->SetDocumentModified();
                        nRetMask = sal_uInt16(true);
                        bAddUndo = true;
                        rReq.Done();
                    }
                    else
                        nRetMask = sal_uInt16(false);
                }
                break;

                case SID_STYLE_HIDE:
                case SID_STYLE_SHOW:
                {
                    if ( pStyleSheet )
                    {
                        pStyleSheet->SetHidden( nSlotId == SID_STYLE_HIDE );
                        InvalidateAttribs();
                        rReq.Done();
                    }
                    else
                        nRetMask = sal_uInt16(false);
                }
                break;

                case SID_STYLE_APPLY:
                {
                    if ( pStyleSheet && !pScMod->GetIsWaterCan() )
                    {
                        GetScDrawView()->ScEndTextEdit();
                        GetScDrawView()->SetStyleSheet(static_cast<SfxStyleSheet*>(pStyleSheet), false);

                        GetScDrawView()->InvalidateAttribs();
                        InvalidateAttribs();
                        rReq.Done();
                    }
                }
                break;

                case SID_STYLE_NEW_BY_EXAMPLE:
                case SID_STYLE_UPDATE_BY_EXAMPLE:
                {
                    if (nSlotId == SID_STYLE_NEW_BY_EXAMPLE)
                    {
                        pStyleSheet = &pStylePool->Make( aStyleName, eFamily, SfxStyleSearchBits::UserDefined );

                        // when a style is present, then this will become
                        // the parent of the new style:
                        if (SfxStyleSheet* pOldStyle = GetDrawView()->GetStyleSheet())
                            pStyleSheet->SetParent(pOldStyle->GetName());
                    }
                    else
                    {
                        pStyleSheet = GetDrawView()->GetStyleSheet();
                        xOldData->InitFromStyle( pStyleSheet );
                    }

                    if ( bUndo )
                    {
                        OUString aUndo = ScResId( STR_UNDO_EDITGRAPHICSTYLE );
                        pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewShellId() );
                        bListAction = true;
                    }

                    SfxItemSet aCoreSet(GetDrawView()->GetModel().GetItemPool());
                    GetDrawView()->GetAttributes(aCoreSet, true);

                    SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet();
                    pStyleSet->Put(aCoreSet);
                    static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));

                    xNewData->InitFromStyle( pStyleSheet );
                    bAddUndo = true;

                    //  call SetStyleSheet after adding the ScUndoModifyStyle
                    //  (pStyleSheet pointer is used!)
                    bStyleToMarked = true;
                    rReq.Done();
                }
                break;
                default:
                    break;
            }
        }
        break;
        default:
            break;
    } // switch ( eFamily )

    // create new or process through Dialog:
    if ( nSlotId == SID_STYLE_NEW || nSlotId == SID_STYLE_EDIT )
    {
        if (pStyleSheet)
        {
            ExecuteStyleEdit(rReq, pStyleSheet, nRetMask, nSlotId, bAddUndo, bUndo,
                    xOldData, xNewData, eFamily, bStyleToMarked, bListAction, pEditObject, aSelection);
            return; // skip calling ExecuteStyleEditPost because we invoked an async dialog
        }
    }

    ExecuteStyleEditPost(rReq, pStyleSheet, nSlotId, nRetMask, bAddUndo, bUndo,
            eFamily, *xOldData, *xNewData, bStyleToMarked, bListAction, pEditObject, aSelection);
}

void ScTabViewShell::ExecuteStyleEdit(SfxRequest& rReq, SfxStyleSheetBase* pStyleSheet, sal_uInt16 nRetMask,
                        sal_uInt16 nSlotId, bool bAddUndo, bool bUndo,
                        const std::shared_ptr<ScStyleSaveData>& rOldData,
                        const std::shared_ptr<ScStyleSaveData>& rNewData, SfxStyleFamily eFamily,
                        bool bStyleToMarked, bool bListAction,
                        SdrObject* pEditObject, ESelection aSelection)
{
    ScDocShell*     pDocSh      = GetViewData().GetDocShell();
    ScDocument&     rDoc        = pDocSh->GetDocument();
    SfxStyleFamily  eFam = pStyleSheet->GetFamily();
    VclPtr<SfxAbstractTabDialog> pDlg;
    bool bPage = false;

    // Store old Items from the style
    std::shared_ptr<SfxItemSet> xOldSet = std::make_shared<SfxItemSet>(pStyleSheet->GetItemSet());
    OUString aOldName = pStyleSheet->GetName();

    switch ( eFam )
    {
        case SfxStyleFamily::Page:
            bPage = true;
            break;

        case SfxStyleFamily::Para:
            {
                SfxItemSet& rSet = pStyleSheet->GetItemSet();

                if ( const SfxUInt32Item* pItem = rSet.GetItemIfSet( ATTR_VALUE_FORMAT,
                        false ) )
                {
                    // Produce and format NumberFormat Value from Value and Language
                    sal_uLong nFormat = pItem->GetValue();
                    LanguageType eLang =
                        rSet.Get(ATTR_LANGUAGE_FORMAT ).GetLanguage();
                    sal_uLong nLangFormat = rDoc.GetFormatTable()->
                        GetFormatForLanguageIfBuiltIn( nFormat, eLang );
                    if ( nLangFormat != nFormat )
                    {
                        SfxUInt32Item aNewItem( ATTR_VALUE_FORMAT, nLangFormat );
                        rSet.Put( aNewItem );
                        xOldSet->Put( aNewItem );
                        // Also in aOldSet for comparison after the  dialog,
                        // Otherwise might miss a language change
                    }
                }

                std::unique_ptr<SvxNumberInfoItem> pNumberInfoItem(
                    ScTabViewShell::MakeNumberInfoItem(rDoc, GetViewData()));

                pDocSh->PutItem( *pNumberInfoItem );
                bPage = false;

                // Definitely a SvxBoxInfoItem with Table = sal_False in set:
                // (If there is no item, the dialogue will also delete the
                // BORDER_OUTER SvxBoxItem from the Template Set)
                if ( rSet.GetItemState( ATTR_BORDER_INNER, false ) != SfxItemState::SET )
                {
                    SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER );
                    aBoxInfoItem.SetTable(false);       // no inner lines
                    aBoxInfoItem.SetDist(true);
                    aBoxInfoItem.SetMinDist(false);
                    rSet.Put( aBoxInfoItem );
                }
            }
            break;

        case SfxStyleFamily::Frame:
        default:
            break;
    }

    SetInFormatDialog(true);

    SfxItemSet& rStyleSet = pStyleSheet->GetItemSet();
    rStyleSet.MergeRange( XATTR_FILL_FIRST, XATTR_FILL_LAST );

    ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();

    weld::Window* pDialogParent = rReq.GetFrameWeld();
    if (!pDialogParent)
        pDialogParent = GetFrameWeld();

    if (eFam == SfxStyleFamily::Frame)
        pDlg = pFact->CreateScDrawStyleDlg(pDialogParent, *pStyleSheet, GetDrawView());
    else
        pDlg = pFact->CreateScStyleDlg(pDialogParent, *pStyleSheet, bPage);

    auto xRequest = std::make_shared<SfxRequest>(rReq);
    rReq.Ignore(); // the 'old' request is not relevant any more
    pDlg->StartExecuteAsync(
        [this, pDlg, xRequest=std::move(xRequest), pStyleSheet,
            nRetMask, xOldSet=std::move(xOldSet), nSlotId, bAddUndo, bUndo,
            xOldData = rOldData, xNewData = rNewData, aOldName, eFamily, bStyleToMarked,
            bListAction, pEditObject, aSelection]
        (sal_Int32 nResult) mutable -> void
        {
            SetInFormatDialog(false);
            ExecuteStyleEditDialog(pDlg, pStyleSheet, nResult, nRetMask, xOldSet, nSlotId,
                    bAddUndo, *xNewData, aOldName);
            pDlg->disposeOnce();
            ExecuteStyleEditPost(*xRequest, pStyleSheet, nSlotId, nRetMask, bAddUndo, bUndo, eFamily,
                    *xOldData, *xNewData, bStyleToMarked, bListAction, pEditObject, aSelection);
        }
    );
}

void ScTabViewShell::ExecuteStyleEditDialog(VclPtr<SfxAbstractTabDialog> pDlg,
                        SfxStyleSheetBase* pStyleSheet, sal_uInt16 nResult,
                        sal_uInt16& rnRetMask, std::shared_ptr<SfxItemSet> xOldSet, const sal_uInt16 nSlotId,
                        bool& rbAddUndo, ScStyleSaveData& rNewData, std::u16string_view aOldName)
{
    ScDocShell*     pDocSh = GetViewData().GetDocShell();
    ScDocument&     rDoc = pDocSh->GetDocument();
    SfxBindings&    rBindings = GetViewData().GetBindings();
    SfxStyleSheetBasePool* pStylePool = rDoc.GetStyleSheetPool();
    SfxStyleFamily  eFam = pStyleSheet->GetFamily();
    if ( nResult == RET_OK )
    {
        const SfxItemSet* pOutSet = pDlg->GetOutputItemSet();

        if ( pOutSet )
        {
            rnRetMask = sal_uInt16(pStyleSheet->GetMask());

            // Attribute comparisons (earlier in ModifyStyleSheet) now here
            // with the old values (the style is already changed)
            if ( SfxStyleFamily::Para == eFam )
            {
                SfxItemSet& rNewSet = pStyleSheet->GetItemSet();
                bool bNumFormatChanged;
                if ( ScGlobal::CheckWidthInvalidate(
                                    bNumFormatChanged, rNewSet, *xOldSet ) )
                    rDoc.InvalidateTextWidth( nullptr, nullptr, bNumFormatChanged );

                SCTAB nTabCount = rDoc.GetTableCount();
                for (SCTAB nTab=0; nTab<nTabCount; nTab++)
                    rDoc.SetStreamValid(nTab, false);

                sal_uLong nOldFormat = xOldSet->Get( ATTR_VALUE_FORMAT ).GetValue();
                sal_uLong nNewFormat = rNewSet.Get( ATTR_VALUE_FORMAT ).GetValue();
                if ( nNewFormat != nOldFormat )
                {
                    ScInterpreterContext& rContext = rDoc.GetNonThreadedContext();
                    const SvNumberformat* pOld = rContext.NFGetFormatEntry( nOldFormat );
                    const SvNumberformat* pNew = rContext.NFGetFormatEntry( nNewFormat );
                    if ( pOld && pNew && pOld->GetLanguage() != pNew->GetLanguage() )
                        rNewSet.Put( SvxLanguageItem(
                                        pNew->GetLanguage(), ATTR_LANGUAGE_FORMAT ) );
                }

                rDoc.getCellAttributeHelper().CellStyleCreated(rDoc, pStyleSheet->GetName());
            }
            else if ( SfxStyleFamily::Page == eFam )
            {
                //! Here also queries for Page Styles

                OUString aNewName = pStyleSheet->GetName();
                if ( aNewName != aOldName &&
                        rDoc.RenamePageStyleInUse( aOldName, aNewName ) )
                {
                    rBindings.Invalidate( SID_STATUS_PAGESTYLE );
                    rBindings.Invalidate( FID_RESET_PRINTZOOM );
                }

                rDoc.ModifyStyleSheet( *pStyleSheet, *pOutSet );
                rBindings.Invalidate( FID_RESET_PRINTZOOM );
            }
            else
            {
                SfxItemSet& rAttr = pStyleSheet->GetItemSet();
                sdr::properties::CleanupFillProperties(rAttr);

                // check for unique names of named items for xml
                auto checkForUniqueItem = [&] (auto nWhichId)
                {
                    if (auto pOldItem = rAttr.GetItemIfSet(nWhichId, false))
                    {
                        if (auto pNewItem = pOldItem->checkForUniqueItem(GetDrawView()->GetModel()))
                            rAttr.Put(std::move(pNewItem));
                    }
                };

                checkForUniqueItem(XATTR_FILLBITMAP);
                checkForUniqueItem(XATTR_LINEDASH);
                checkForUniqueItem(XATTR_LINESTART);
                checkForUniqueItem(XATTR_LINEEND);
                checkForUniqueItem(XATTR_FILLGRADIENT);
                checkForUniqueItem(XATTR_FILLFLOATTRANSPARENCE);
                checkForUniqueItem(XATTR_FILLHATCH);

                static_cast<SfxStyleSheet*>(pStyleSheet)->Broadcast(SfxHint(SfxHintId::DataChanged));
                GetScDrawView()->InvalidateAttribs();
            }

            pDocSh->SetDocumentModified();

            if ( SfxStyleFamily::Para == eFam )
            {
                ScTabViewShell::UpdateNumberFormatter(
                        *( pDocSh->GetItem(SID_ATTR_NUMBERFORMAT_INFO) ));

                UpdateStyleSheetInUse( pStyleSheet );
                InvalidateAttribs();
            }

            rNewData.InitFromStyle( pStyleSheet );
            rbAddUndo = true;
        }
    }
    else
    {
        if ( nSlotId == SID_STYLE_NEW )
            pStylePool->Remove( pStyleSheet );
        else
        {
            // If in the meantime something was painted with the
            // temporary changed item set
            pDocSh->PostPaintGridAll();
        }
    }
}

void ScTabViewShell::ExecuteStyleEditPost(SfxRequest& rReq, SfxStyleSheetBase* pStyleSheet,
                        sal_uInt16 nSlotId, sal_uInt16 nRetMask, bool bAddUndo, bool bUndo,
                        const SfxStyleFamily eFamily,
                        ScStyleSaveData& rOldData, ScStyleSaveData& rNewData,
                        bool bStyleToMarked, bool bListAction,
                        SdrObject* pEditObject, ESelection aSelection)
{
    ScDocShell*     pDocSh = GetViewData().GetDocShell();

    rReq.SetReturnValue( SfxUInt16Item( nSlotId, nRetMask ) );

    if ( bAddUndo && bUndo)
        pDocSh->GetUndoManager()->AddUndoAction(
                    std::make_unique<ScUndoModifyStyle>( *pDocSh, eFamily, rOldData, rNewData ) );

    if ( bStyleToMarked )
    {
        //  call SetStyleSheetToMarked after adding the ScUndoModifyStyle,
        //  so redo will find the modified style
        if (eFamily == SfxStyleFamily::Para)
        {
            SetStyleSheetToMarked( static_cast<SfxStyleSheet*>(pStyleSheet) );
        }
        else if (eFamily == SfxStyleFamily::Frame)
        {
            GetScDrawView()->ScEndTextEdit();
            GetScDrawView()->SetStyleSheet( static_cast<SfxStyleSheet*>(pStyleSheet), false );
        }
        InvalidateAttribs();
    }

    if ( bListAction )
        pDocSh->GetUndoManager()->LeaveListAction();

    // The above call to ScEndTextEdit left us in an inconsistent state:
    // Text editing isn't active, but the text edit shell still is. And we
    // couldn't just deactivate it fully, because in case of editing a
    // comment, that will make the comment disappear. So let's try to
    // reactivate text editing instead:
    auto pFuText = dynamic_cast<FuText*>(GetDrawFuncPtr());
    if (pFuText && pEditObject != GetDrawView()->GetTextEditObject())
    {
        pFuText->SetInEditMode(pEditObject);
        if (GetDrawView()->GetTextEditOutlinerView())
            GetDrawView()->GetTextEditOutlinerView()->SetSelection(aSelection);
    }
}

void ScTabViewShell::GetStyleState( SfxItemSet& rSet )
{
    ScDocument&             rDoc          = GetViewData().GetDocument();
    SfxStyleSheetBasePool*  pStylePool    = rDoc.GetStyleSheetPool();

    bool bProtected = false;
    SCTAB nTabCount = rDoc.GetTableCount();
    for (SCTAB i=0; i<nTabCount && !bProtected; i++)
        if (rDoc.IsTabProtected(i))                // look after protected table
            bProtected = true;

    SfxWhichIter    aIter(rSet);
    sal_uInt16          nWhich = aIter.FirstWhich();
    sal_uInt16          nSlotId = 0;

    while ( nWhich )
    {
        nSlotId = SfxItemPool::IsWhich( nWhich )
                    ? GetPool().GetSlotId( nWhich )
                    : nWhich;

        switch ( nSlotId )
        {
            case SID_STYLE_APPLY:
                if ( !pStylePool )
                    rSet.DisableItem( nSlotId );
                break;

            case SID_STYLE_FAMILY2:     // cell style sheets
            {
                SfxStyleSheet* pStyleSheet = const_cast<SfxStyleSheet*>(GetStyleSheetFromMarked());

                if ( pStyleSheet )
                    rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
                else
                    rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
            }
            break;

            case SID_STYLE_FAMILY3:     // drawing style sheets
            {
                SfxStyleSheet* pStyleSheet = GetDrawView()->GetStyleSheet();

                if ( pStyleSheet )
                    rSet.Put( SfxTemplateItem( nSlotId, pStyleSheet->GetName() ) );
                else
                    rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
            }
            break;

            case SID_STYLE_FAMILY4:     // page style sheets
            {
                SCTAB           nCurTab     = GetViewData().CurrentTabForData();
                OUString        aPageStyle  = rDoc.GetPageStyle( nCurTab );
                SfxStyleSheet*  pStyleSheet = pStylePool ? static_cast<SfxStyleSheet*>(pStylePool->
                                    Find( aPageStyle, SfxStyleFamily::Page )) : nullptr;

                if ( pStyleSheet )
                    rSet.Put( SfxTemplateItem( nSlotId, aPageStyle ) );
                else
                    rSet.Put( SfxTemplateItem( nSlotId, OUString() ) );
            }
            break;

            case SID_STYLE_WATERCAN:
            {
                rSet.Put(SfxBoolItem(nSlotId, ScModule::get()->GetIsWaterCan()));
            }
            break;

            case SID_STYLE_UPDATE_BY_EXAMPLE:
            {
                std::unique_ptr<SfxUInt16Item> pFamilyItem;
                GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);

                bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());

                if ( bProtected || bPage )
                    rSet.DisableItem( nSlotId );
            }
            break;

            case SID_STYLE_EDIT:
            case SID_STYLE_DELETE:
            case SID_STYLE_HIDE:
            case SID_STYLE_SHOW:
            {
                std::unique_ptr<SfxUInt16Item> pFamilyItem;
                GetViewFrame().GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem);
                bool bPage = pFamilyItem && SfxStyleFamily::Page == static_cast<SfxStyleFamily>(pFamilyItem->GetValue());

                if ( bProtected && !bPage )
                    rSet.DisableItem( nSlotId );
            }
            break;

            default:
                break;
        }

        nWhich = aIter.NextWhich();
    }
}

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