/* -*- 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 <sal/config.h>
 
#include <formdata.hxx>
 
#include <sfx2/app.hxx>
#include <svx/dialogs.hrc>
#include <svx/extrusionbar.hxx>
#include <svx/fontworkbar.hxx>
#include <editeng/borderline.hxx>
#include <svx/fmshell.hxx>
#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/ipclient.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/docfile.hxx>
#include <tools/svborder.hxx>
 
#include <IAnyRefDialog.hxx>
#include <tabvwsh.hxx>
#include <sc.hrc>
#include <globstr.hrc>
#include <docsh.hxx>
#include <scmod.hxx>
#include <appoptio.hxx>
#include <drawsh.hxx>
#include <drformsh.hxx>
#include <editsh.hxx>
#include <pivotsh.hxx>
#include <SparklineShell.hxx>
#include <auditsh.hxx>
#include <drtxtob.hxx>
#include <inputhdl.hxx>
#include <editutil.hxx>
#include <inputopt.hxx>
#include <inputwin.hxx>
#include <dbdata.hxx>
#include <reffact.hxx>
#include <viewuno.hxx>
#include <dispuno.hxx>
#include <chgtrack.hxx>
#include <cellsh.hxx>
#include <oleobjsh.hxx>
#include <chartsh.hxx>
#include <graphsh.hxx>
#include <mediash.hxx>
#include <pgbrksh.hxx>
#include <dpobject.hxx>
#include <prevwsh.hxx>
#include <scextopt.hxx>
#include <drawview.hxx>
#include <fupoor.hxx>
#include <navsett.hxx>
#include <scabstdlg.hxx>
#include <externalrefmgr.hxx>
#include <defaultsoptions.hxx>
#include <markdata.hxx>
#include <preview.hxx>
#include <documentlinkmgr.hxx>
#include <gridwin.hxx>
 
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/configuration/theDefaultProvider.hpp>
#include <com/sun/star/sheet/XCellRangeMovement.hpp>
#include <com/sun/star/sheet/XCellRangeData.hpp>
#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
#include <comphelper/processfactory.hxx>
#include <sfx2/lokhelper.hxx>
#include <comphelper/flagguard.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
#include <sfx2/sidebar/SidebarController.hxx>
 
using namespace com::sun::star;
using namespace sfx2::sidebar;
 
namespace {
 
bool inChartOrMathContext(const ScTabViewShell* pViewShell)
{
    SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell);
    if (pSidebar)
        return pSidebar->hasChartOrMathContextCurrently();
 
    return false;
}
 
} // anonymous namespace
 
void ScTabViewShell::Activate(bool bMDI)
{
    SfxViewShell::Activate(bMDI);
    bIsActive = true;
    // here no GrabFocus, otherwise there will be problems when something is edited inplace!
 
    if ( bMDI )
    {
        // for input row (ClearCache)
        ScModule* pScMod = SC_MOD();
        pScMod->ViewShellChanged(/*bStopEditing=*/ !comphelper::LibreOfficeKit::isActive());
 
        ActivateView( true, bFirstActivate );
 
        // update AutoCorrect, if Writer has newly created this
        UpdateDrawTextOutliner();
 
        // RegisterNewTargetNames does not exist anymore
 
        SfxViewFrame& rThisFrame  = GetViewFrame();
        if ( mpInputHandler && rThisFrame.HasChildWindow(FID_INPUTLINE_STATUS) )
        {
            // actually only required for Reload (last version):
            // The InputWindow remains, but the View along with the InputHandler is newly created,
            // that is why the InputHandler must be set at the InputWindow.
            SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_INPUTLINE_STATUS);
            if (pChild)
            {
                ScInputWindow* pWin = static_cast<ScInputWindow*>(pChild->GetWindow());
                if (pWin && pWin->IsVisible())
                {
                    pWin->NumLinesChanged(); // tdf#150664
                    ScInputHandler* pOldHdl=pWin->GetInputHandler();
 
                    SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
                    while ( pSh!=nullptr && pOldHdl!=nullptr)
                    {
                        // Hmm, what if pSh is a shell for a different document? But as this code
                        // does not seem to be LibreOfficeKit-specific, probably that doesn't
                        // happen, because having multiple documents open simultaneously has of
                        // course not been a problem at all in traditional desktop LibreOffice.
                        // (Unlike in a LibreOfficeKit-based process where it has been a problem.)
                        if (static_cast<ScTabViewShell*>(pSh)->GetInputHandler() == pOldHdl)
                        {
                            pOldHdl->ResetDelayTimer();
                            break;
                        }
                        pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
                    }
 
                    pWin->SetInputHandler( mpInputHandler.get() );
                }
            }
        }
 
        bool isLOK = comphelper::LibreOfficeKit::isActive();
        UpdateInputHandler( /*bForce=*/ !isLOK, /*bStopEditing=*/ !isLOK );
 
        if ( bFirstActivate )
        {
            SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScNavigatorUpdateAll ) );
            bFirstActivate = false;
 
            // ReadExtOptions (view settings from Excel import) must also be done
            // after the ctor, because of the potential calls to Window::Show.
            // Even after a bugfix (Window::Show no longer notifies the access
            // bridge, it's done in ImplSetReallyVisible), there are problems if Window::Show
            // is called during the ViewShell ctor and reschedules asynchronous calls
            // (for example from the FmFormShell ctor).
            ScExtDocOptions* pExtOpt = GetViewData().GetDocument().GetExtDocOptions();
            if ( pExtOpt && pExtOpt->IsChanged() )
            {
                GetViewData().ReadExtOptions(*pExtOpt);        // Excel view settings
                SetTabNo( GetViewData().GetTabNo(), true );
                pExtOpt->SetChanged( false );
            }
        }
 
        pScActiveViewShell = this;
 
        ScInputHandler* pHdl = pScMod->GetInputHdl(this);
        if (pHdl)
        {
            pHdl->SetRefScale( GetViewData().GetZoomX(), GetViewData().GetZoomY() );
        }
 
        // update change dialog
 
        if ( rThisFrame.HasChildWindow(FID_CHG_ACCEPT) )
        {
            SfxChildWindow* pChild = rThisFrame.GetChildWindow(FID_CHG_ACCEPT);
            if (pChild)
            {
                static_cast<ScAcceptChgDlgWrapper*>(pChild)->ReInitDlg();
            }
        }
 
        if(pScMod->IsRefDialogOpen())
        {
            sal_uInt16 nModRefDlgId=pScMod->GetCurRefDlgId();
            SfxChildWindow* pChildWnd = rThisFrame.GetChildWindow( nModRefDlgId );
            if ( pChildWnd )
            {
                if (auto pController = pChildWnd->GetController())
                {
                    IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pController.get());
                    if (pRefDlg)
                        pRefDlg->ViewShellChanged();
                }
            }
        }
    }
 
    //  don't call CheckSelectionTransfer here - activating a view should not change the
    //  primary selection (may be happening just because the mouse was moved over the window)
 
    if (!inChartOrMathContext(this))
    {
        ContextChangeEventMultiplexer::NotifyContextChange(
            GetController(),
            vcl::EnumContext::Context::Default);
    }
}
 
void ScTabViewShell::Deactivate(bool bMDI)
{
    HideTip();
 
    ScDocument& rDoc = GetViewData().GetDocument();
 
    ScChangeTrack* pChanges = rDoc.GetChangeTrack();
 
    if(pChanges!=nullptr)
    {
        Link<ScChangeTrack&,void> aLink;
        pChanges->SetModifiedLink(aLink);
    }
 
    SfxViewShell::Deactivate(bMDI);
    bIsActive = false;
    ScInputHandler* pHdl = SC_MOD()->GetInputHdl(this);
 
    if( bMDI && !comphelper::LibreOfficeKit::isActive())
    {
        //  during shell deactivation, shells must not be switched, or the loop
        //  through the shell stack (in SfxDispatcher::DoDeactivate_Impl) will not work
        bool bOldDontSwitch = bDontSwitch;
        bDontSwitch = true;
 
        ActivateView( false, false );
 
        if ( GetViewFrame().GetFrame().IsInPlace() ) // inplace
            GetViewData().GetDocShell()->UpdateOle(GetViewData(), true);
 
        if ( pHdl )
            pHdl->NotifyChange( nullptr, true ); // timer-delayed due to document switching
 
        if (pScActiveViewShell == this)
            pScActiveViewShell = nullptr;
 
        bDontSwitch = bOldDontSwitch;
    }
    else
    {
        HideNoteMarker();           // note marker
 
        // in LOK case this could be triggered on every action from other view (doc_setView)
        // we don't want to hide tooltip only because other view did some action
        if ( pHdl && !comphelper::LibreOfficeKit::isActive() )
            pHdl->HideTip();        // Hide formula auto input tip
    }
}
 
void ScTabViewShell::SetActive()
{
    // SFX-View would like to activate itself, since then magical things would happen
    // (eg else the designer may crash)
    ActiveGrabFocus();
}
 
bool ScTabViewShell::PrepareClose(bool bUI)
{
    comphelper::FlagRestorationGuard aFlagGuard(bInPrepareClose, true);
 
    // Call EnterHandler even in formula mode here,
    // so a formula change in an embedded object isn't lost
    // (ScDocShell::PrepareClose isn't called then).
    ScInputHandler* pHdl = SC_MOD()->GetInputHdl( this );
    if ( pHdl && pHdl->IsInputMode() )
    {
        pHdl->EnterHandler();
    }
 
    // draw text edit mode must be closed
    FuPoor* pPoor = GetDrawFuncPtr();
    if (pPoor && IsDrawTextShell())
    {
        // "clean" end of text edit, including note handling, subshells and draw func switching,
        // as in FuDraw and ScTabView::DrawDeselectAll
        GetViewData().GetDispatcher().Execute( pPoor->GetSlotID(), SfxCallMode::SLOT | SfxCallMode::RECORD );
    }
    ScDrawView* pDrView = GetScDrawView();
    if ( pDrView )
    {
        // force end of text edit, to be safe
        // ScEndTextEdit must always be used, to ensure correct UndoManager
        pDrView->ScEndTextEdit();
    }
 
    if ( pFormShell )
    {
        bool bRet = pFormShell->PrepareClose(bUI);
        if (!bRet)
            return bRet;
    }
    return SfxViewShell::PrepareClose(bUI);
}
 
// calculate zoom for in-place
// from the ratio of VisArea and window size of GridWin
 
void ScTabViewShell::UpdateOleZoom()
{
    ScDocShell* pDocSh = GetViewData().GetDocShell();
    if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        //TODO/LATER: is there a difference between the two GetVisArea methods?
        Size aObjSize = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea().GetSize();
        if ( !aObjSize.IsEmpty() )
        {
            vcl::Window* pWin = GetActiveWin();
            Size aWinHMM = pWin->PixelToLogic(pWin->GetOutputSizePixel(), MapMode(MapUnit::Map100thMM));
            SetZoomFactor( Fraction( aWinHMM.Width(),aObjSize.Width() ),
                            Fraction( aWinHMM.Height(),aObjSize.Height() ) );
        }
    }
}
 
void ScTabViewShell::InnerResizePixel( const Point &rOfs, const Size &rSize, bool inplaceEditModeChange )
{
    Size aNewSize( rSize );
    if ( GetViewFrame().GetFrame().IsInPlace() )
    {
        SvBorder aBorder;
        GetBorderSize( aBorder, rSize );
        SetBorderPixel( aBorder );
 
        Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
 
        Size aSize( rSize );
        aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) );
        aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) );
 
        if ( !aObjSize.IsEmpty() )
        {
            Size aLogicSize = GetWindow()->PixelToLogic(aSize, MapMode(MapUnit::Map100thMM));
            SfxViewShell::SetZoomFactor( Fraction( aLogicSize.Width(),aObjSize.Width() ),
                            Fraction( aLogicSize.Height(),aObjSize.Height() ) );
        }
 
        Point aPos( rOfs );
        aPos.AdjustX(aBorder.Left() );
        aPos.AdjustY(aBorder.Top() );
        GetWindow()->SetPosSizePixel( aPos, aSize );
    }
    else
    {
        SvBorder aBorder;
        GetBorderSize( aBorder, rSize );
        SetBorderPixel( aBorder );
        aNewSize.AdjustWidth(aBorder.Left() + aBorder.Right() );
        aNewSize.AdjustHeight(aBorder.Top() + aBorder.Bottom() );
    }
 
    DoResize( rOfs, aNewSize, true );                   // rSize = size of gridwin
 
    UpdateOleZoom();                                    // calculate zoom for in-place
 
    if (!inplaceEditModeChange)
    {
        GetViewData().GetDocShell()->SetDocumentModified();
    }
}
 
void ScTabViewShell::OuterResizePixel( const Point &rOfs, const Size &rSize )
{
    SvBorder aBorder;
    GetBorderSize( aBorder, rSize );
    SetBorderPixel( aBorder );
 
    DoResize( rOfs, rSize );                    // position and size of tabview as passed
 
    // ForceMove as replacement for Sfx-Move mechanism
    // (aWinPos must be kept current, so that ForceMove works for Ole deactivation)
 
    ForceMove();
}
 
void ScTabViewShell::SetZoomFactor( const Fraction &rZoomX, const Fraction &rZoomY )
{
    // for OLE...
 
    Fraction aFrac20( 1,5 );
    Fraction aFrac400( 4,1 );
 
    Fraction aNewX( rZoomX );
    if ( aNewX < aFrac20 )
        aNewX = aFrac20;
    if ( aNewX > aFrac400 )
        aNewX = aFrac400;
    Fraction aNewY( rZoomY );
    if ( aNewY < aFrac20 )
        aNewY = aFrac20;
    if ( aNewY > aFrac400 )
        aNewY = aFrac400;
 
    GetViewData().UpdateScreenZoom( aNewX, aNewY );
    SetZoom( aNewX, aNewY, true );
 
    PaintGrid();
    PaintTop();
    PaintLeft();
 
    SfxViewShell::SetZoomFactor( rZoomX, rZoomY );
}
 
void ScTabViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
{
    // adjust to entire cells (in 1/100 mm)
 
    Size aPixelSize = rRect.GetSize();
    vcl::Window* pWin = const_cast<ScTabViewShell*>(this)->GetActiveWin();
    Size aLogicSize = pWin->PixelToLogic( aPixelSize );
 
    const ScViewData& rViewData = GetViewData();
    ScDocument& rDoc = rViewData.GetDocument();
    ScSplitPos ePos = rViewData.GetActivePart();
    SCCOL nCol = rViewData.GetPosX(WhichH(ePos));
    SCROW nRow = rViewData.GetPosY(WhichV(ePos));
    SCTAB nTab = rViewData.GetTabNo();
    bool bNegativePage = rDoc.IsNegativePage( nTab );
 
    tools::Rectangle aLogicRect = rDoc.GetMMRect( nCol, nRow, nCol, nRow, nTab );
    if ( bNegativePage )
    {
        // use right edge of aLogicRect, and aLogicSize
        aLogicRect.SetLeft( aLogicRect.Right() - aLogicSize.Width() + 1 );    // Right() is set below
    }
    aLogicRect.SetSize( aLogicSize );
 
    rViewData.GetDocShell()->SnapVisArea( aLogicRect );
 
    rRect.SetSize( pWin->LogicToPixel( aLogicRect.GetSize() ) );
}
 
void ScTabViewShell::Move()
{
    Point aNewPos = GetViewFrame().GetWindow().OutputToScreenPixel(Point());
 
    if (aNewPos != aWinPos)
    {
        StopMarking();
        aWinPos = aNewPos;
    }
}
 
void ScTabViewShell::ShowCursor(bool /* bOn */)
{
/*!!!   ShowCursor is not called as a pair as in gridwin.
        here the CursorLockCount for Gridwin must be set directly to 0
 
    if (bOn)
        ShowAllCursors();
    else
        HideAllCursors();
*/
}
 
void ScTabViewShell::WriteUserData(OUString& rData, bool /* bBrowse */)
{
    GetViewData().WriteUserData(rData);
}
 
void ScTabViewShell::WriteUserDataSequence (uno::Sequence < beans::PropertyValue >& rSettings )
{
    GetViewData().WriteUserDataSequence(rSettings);
}
 
void ScTabViewShell::ReadUserData(const OUString& rData, bool /* bBrowse */)
{
    if ( !GetViewData().GetDocShell()->IsPreview() )
        DoReadUserData( rData );
}
 
void ScTabViewShell::ReadUserDataSequence (const uno::Sequence < beans::PropertyValue >& rSettings )
{
    if ( !GetViewData().GetDocShell()->IsPreview() )
        DoReadUserDataSequence( rSettings );
}
 
void ScTabViewShell::DoReadUserDataSequence( const uno::Sequence < beans::PropertyValue >& rSettings )
{
    vcl::Window* pOldWin = GetActiveWin();
    bool bFocus = pOldWin && pOldWin->HasFocus();
 
    GetViewData().ReadUserDataSequence(rSettings);
    SetTabNo( GetViewData().GetTabNo(), true );
 
    if ( GetViewData().IsPagebreakMode() )
        SetCurSubShell( GetCurObjectSelectionType(), true );
 
    vcl::Window* pNewWin = GetActiveWin();
    if (pNewWin && pNewWin != pOldWin)
    {
        SetWindow( pNewWin );       //! is this ViewShell always active???
        if (bFocus)
            pNewWin->GrabFocus();
        WindowChanged();            // drawing layer (for instance #56771#)
    }
 
    if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
        GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
    {
        InvalidateSplit();
    }
 
    ZoomChanged();
 
    TestHintWindow();
 
    //! if ViewData has more tables than document, remove tables in ViewData
}
 
// DoReadUserData is also called from ctor when switching from print preview
 
void ScTabViewShell::DoReadUserData( std::u16string_view rData )
{
    vcl::Window* pOldWin = GetActiveWin();
    bool bFocus = pOldWin && pOldWin->HasFocus();
 
    GetViewData().ReadUserData(rData);
    SetTabNo( GetViewData().GetTabNo(), true );
 
    if ( GetViewData().IsPagebreakMode() )
        SetCurSubShell( GetCurObjectSelectionType(), true );
 
    vcl::Window* pNewWin = GetActiveWin();
    if (pNewWin && pNewWin != pOldWin)
    {
        SetWindow( pNewWin );       //! is this ViewShell always active???
        if (bFocus)
            pNewWin->GrabFocus();
        WindowChanged();            // drawing layer (for instance #56771#)
    }
 
    if (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
        GetViewData().GetVSplitMode() == SC_SPLIT_FIX)
    {
        InvalidateSplit();
    }
 
    ZoomChanged();
 
    TestHintWindow();
 
    //! if ViewData has more tables than document, remove tables in ViewData
}
 
void ScTabViewShell::UpdateDrawShell()
{
    // Called after user interaction that may delete the selected drawing object.
    // Remove DrawShell if nothing is selected.
 
    SdrView* pDrView = GetScDrawView();
    if ( pDrView && pDrView->GetMarkedObjectList().GetMarkCount() == 0 && !IsDrawSelMode() )
        SetDrawShell( false );
}
 
void ScTabViewShell::SetDrawShellOrSub()
{
    bActiveDrawSh = true;
 
    if(bActiveDrawFormSh)
    {
        SetCurSubShell(OST_DrawForm);
    }
    else if(bActiveGraphicSh)
    {
        SetCurSubShell(OST_Graphic);
    }
    else if(bActiveMediaSh)
    {
        SetCurSubShell(OST_Media);
    }
    else if(bActiveChartSh)
    {
        SetCurSubShell(OST_Chart);
    }
    else if(bActiveOleObjectSh)
    {
        SetCurSubShell(OST_OleObject);
    }
    else
    {
        SetCurSubShell(OST_Drawing, true /* force: different toolbars are
                                            visible concerning shape type
                                            and shape state */);
    }
}
 
void ScTabViewShell::SetDrawShell( bool bActive )
{
    if(bActive)
    {
        SetCurSubShell(OST_Drawing, true /* force: different toolbars are
                                            visible concerning shape type
                                            and shape state */);
    }
    else
    {
        if(bActiveDrawFormSh || bActiveDrawSh ||
            bActiveGraphicSh || bActiveMediaSh || bActiveOleObjectSh||
            bActiveChartSh || bActiveDrawTextSh)
        {
            SetCurSubShell(OST_Cell);
        }
        bActiveDrawFormSh=false;
        bActiveGraphicSh=false;
        bActiveMediaSh=false;
        bActiveOleObjectSh=false;
        bActiveChartSh=false;
    }
 
    bool bWasDraw = bActiveDrawSh || bActiveDrawTextSh;
 
    bActiveDrawSh = bActive;
    bActiveDrawTextSh = false;
 
    if ( !bActive )
    {
        ResetDrawDragMode();        // switch off Mirror / Rotate
 
        if (bWasDraw && (GetViewData().GetHSplitMode() == SC_SPLIT_FIX ||
                         GetViewData().GetVSplitMode() == SC_SPLIT_FIX))
        {
            // adjust active part to cursor, etc.
            MoveCursorAbs( GetViewData().GetCurX(), GetViewData().GetCurY(),
                            SC_FOLLOW_NONE, false, false, true );
        }
    }
}
 
void ScTabViewShell::SetDrawTextShell( bool bActive )
{
    bActiveDrawTextSh = bActive;
    if ( bActive )
    {
        bActiveDrawFormSh=false;
        bActiveGraphicSh=false;
        bActiveMediaSh=false;
        bActiveOleObjectSh=false;
        bActiveChartSh=false;
        bActiveDrawSh = false;
        SetCurSubShell(OST_DrawText);
    }
    else
        SetCurSubShell(OST_Cell);
 
}
 
void ScTabViewShell::SetPivotShell( bool bActive )
{
    //  SetPivotShell is called from CursorPosChanged every time
    //  -> don't change anything except switching between cell and pivot shell
 
    if (eCurOST != OST_Pivot && eCurOST != OST_Cell)
        return;
 
    if ( bActive )
    {
        bActiveDrawTextSh = bActiveDrawSh = false;
        bActiveDrawFormSh=false;
        bActiveGraphicSh=false;
        bActiveMediaSh=false;
        bActiveOleObjectSh=false;
        bActiveChartSh=false;
        SetCurSubShell(OST_Pivot);
    }
    else
        SetCurSubShell(OST_Cell);
}
 
void ScTabViewShell::SetSparklineShell(bool bActive)
{
    if (eCurOST != OST_Sparkline && eCurOST != OST_Cell)
        return;
 
    if (bActive)
    {
        bActiveDrawTextSh = bActiveDrawSh = false;
        bActiveDrawFormSh=false;
        bActiveGraphicSh=false;
        bActiveMediaSh=false;
        bActiveOleObjectSh=false;
        bActiveChartSh=false;
        SetCurSubShell(OST_Sparkline);
    }
    else
        SetCurSubShell(OST_Cell);
}
 
void ScTabViewShell::SetAuditShell( bool bActive )
{
    if ( bActive )
    {
        bActiveDrawTextSh = bActiveDrawSh = false;
        bActiveDrawFormSh=false;
        bActiveGraphicSh=false;
        bActiveMediaSh=false;
        bActiveOleObjectSh=false;
        bActiveChartSh=false;
        SetCurSubShell(OST_Auditing);
    }
    else
        SetCurSubShell(OST_Cell);
}
 
void ScTabViewShell::SetDrawFormShell( bool bActive )
{
    bActiveDrawFormSh = bActive;
 
    if(bActiveDrawFormSh)
        SetCurSubShell(OST_DrawForm);
}
void ScTabViewShell::SetChartShell( bool bActive )
{
    bActiveChartSh = bActive;
 
    if(bActiveChartSh)
        SetCurSubShell(OST_Chart);
}
 
void ScTabViewShell::SetGraphicShell( bool bActive )
{
    bActiveGraphicSh = bActive;
 
    if(bActiveGraphicSh)
        SetCurSubShell(OST_Graphic);
}
 
void ScTabViewShell::SetMediaShell( bool bActive )
{
    bActiveMediaSh = bActive;
 
    if(bActiveMediaSh)
        SetCurSubShell(OST_Media);
}
 
void ScTabViewShell::SetOleObjectShell( bool bActive )
{
    bActiveOleObjectSh = bActive;
 
    if(bActiveOleObjectSh)
        SetCurSubShell(OST_OleObject);
    else
        SetCurSubShell(OST_Cell);
}
 
void ScTabViewShell::SetEditShell(EditView* pView, bool bActive )
{
    if(bActive)
    {
        if (pEditShell)
            pEditShell->SetEditView( pView );
        else
            pEditShell.reset( new ScEditShell(pView, GetViewData()) );
 
        SetCurSubShell(OST_Editing);
    }
    else if(bActiveEditSh)
    {
        SetCurSubShell(OST_Cell);
        GetViewData().SetEditHighlight(false);
    }
    bActiveEditSh = bActive;
}
 
void ScTabViewShell::SetCurSubShell(ObjectSelectionType eOST, bool bForce)
{
    ScViewData& rViewData   = GetViewData();
    ScDocShell* pDocSh      = rViewData.GetDocShell();
 
    if(bDontSwitch) return;
 
    if(!pCellShell) // is anyway always used
    {
        pCellShell.reset(new ScCellShell(GetViewData(), GetFrameWin()));
        pCellShell->SetRepeatTarget( &aTarget );
    }
 
    bool bPgBrk = rViewData.IsPagebreakMode();
 
    if(bPgBrk && !pPageBreakShell)
    {
        pPageBreakShell.reset( new ScPageBreakShell( this ) );
        pPageBreakShell->SetRepeatTarget( &aTarget );
    }
 
    if ( !(eOST!=eCurOST || bForce) )
        return;
 
    bool bCellBrush = false;    // "format paint brush" allowed for cells
    bool bDrawBrush = false;    // "format paint brush" allowed for drawing objects
 
    if(eCurOST!=OST_NONE) RemoveSubShell();
 
    if (pFormShell && !bFormShellAtTop)
        AddSubShell(*pFormShell);               // add below own subshells
 
    switch(eOST)
    {
        case    OST_Cell:
        {
            AddSubShell(*pCellShell);
            if(bPgBrk) AddSubShell(*pPageBreakShell);
            bCellBrush = true;
        }
        break;
        case    OST_Editing:
        {
            AddSubShell(*pCellShell);
            if(bPgBrk) AddSubShell(*pPageBreakShell);
 
            if(pEditShell)
            {
                AddSubShell(*pEditShell);
            }
        }
        break;
        case    OST_DrawText:
        {
            if ( !pDrawTextShell )
            {
                pDocSh->MakeDrawLayer();
                pDrawTextShell.reset( new ScDrawTextObjectBar(GetViewData()) );
            }
            AddSubShell(*pDrawTextShell);
        }
        break;
        case    OST_Drawing:
        {
            if (svx::checkForSelectedCustomShapes(
                        GetScDrawView(), true /* bOnlyExtruded */ )) {
                if (pExtrusionBarShell == nullptr)
                    pExtrusionBarShell.reset( new svx::ExtrusionBar(this) );
                AddSubShell( *pExtrusionBarShell );
            }
 
            if (svx::checkForSelectedFontWork(
                        GetScDrawView() )) {
                if (pFontworkBarShell == nullptr)
                    pFontworkBarShell.reset( new svx::FontworkBar(this) );
                AddSubShell( *pFontworkBarShell );
            }
 
            if ( !pDrawShell )
            {
                pDocSh->MakeDrawLayer();
                pDrawShell.reset(new ScDrawShell(GetViewData()));
                pDrawShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pDrawShell);
            bDrawBrush = true;
        }
        break;
 
        case    OST_DrawForm:
        {
            if ( !pDrawFormShell )
            {
                pDocSh->MakeDrawLayer();
                pDrawFormShell.reset( new ScDrawFormShell(GetViewData()) );
                pDrawFormShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pDrawFormShell);
            bDrawBrush = true;
        }
        break;
 
        case    OST_Chart:
        {
            if ( !pChartShell )
            {
                pDocSh->MakeDrawLayer();
                pChartShell.reset( new ScChartShell(GetViewData()) );
                pChartShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pChartShell);
            bDrawBrush = true;
        }
        break;
 
        case    OST_OleObject:
        {
            if ( !pOleObjectShell )
            {
                pDocSh->MakeDrawLayer();
                pOleObjectShell.reset( new ScOleObjectShell(GetViewData()) );
                pOleObjectShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pOleObjectShell);
            bDrawBrush = true;
        }
        break;
 
        case    OST_Graphic:
        {
            if ( !pGraphicShell)
            {
                pDocSh->MakeDrawLayer();
                pGraphicShell.reset( new ScGraphicShell(GetViewData()) );
                pGraphicShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pGraphicShell);
            bDrawBrush = true;
        }
        break;
 
        case    OST_Media:
        {
            if ( !pMediaShell)
            {
                pDocSh->MakeDrawLayer();
                pMediaShell.reset( new ScMediaShell(GetViewData()) );
                pMediaShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pMediaShell);
        }
        break;
 
        case    OST_Pivot:
        {
            AddSubShell(*pCellShell);
            if(bPgBrk) AddSubShell(*pPageBreakShell);
 
            if ( !pPivotShell )
            {
                pPivotShell.reset( new ScPivotShell( this ) );
                pPivotShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pPivotShell);
            bCellBrush = true;
        }
        break;
        case    OST_Auditing:
        {
            AddSubShell(*pCellShell);
            if(bPgBrk) AddSubShell(*pPageBreakShell);
 
            if ( !pAuditingShell )
            {
                pDocSh->MakeDrawLayer();    // the waiting time rather now as on the click
 
                pAuditingShell.reset( new ScAuditingShell(GetViewData()) );
                pAuditingShell->SetRepeatTarget( &aTarget );
            }
            AddSubShell(*pAuditingShell);
            bCellBrush = true;
        }
        break;
        case OST_Sparkline:
        {
            AddSubShell(*pCellShell);
            if(bPgBrk) AddSubShell(*pPageBreakShell);
 
            if (!m_pSparklineShell)
            {
                m_pSparklineShell.reset(new sc::SparklineShell(this));
                m_pSparklineShell->SetRepeatTarget(&aTarget);
            }
            AddSubShell(*m_pSparklineShell);
            bCellBrush = true;
        }
        break;
        default:
        OSL_FAIL("wrong shell requested");
        break;
    }
 
    if (pFormShell && bFormShellAtTop)
        AddSubShell(*pFormShell);               // add on top of own subshells
 
    eCurOST=eOST;
 
    // abort "format paint brush" when switching to an incompatible shell
    if ( ( GetBrushDocument() && !bCellBrush ) || ( GetDrawBrushSet() && !bDrawBrush ) )
        ResetBrushDocument();
}
 
void ScTabViewShell::SetFormShellAtTop( bool bSet )
{
    if ( pFormShell && !bSet )
        pFormShell->ForgetActiveControl();      // let the FormShell know it no longer has the focus
 
    if ( bFormShellAtTop != bSet )
    {
        bFormShellAtTop = bSet;
        SetCurSubShell( GetCurObjectSelectionType(), true );
    }
}
 
IMPL_LINK_NOARG(ScTabViewShell, FormControlActivated, LinkParamNone*, void)
{
    // a form control got the focus, so the form shell has to be on top
    SetFormShellAtTop( true );
}
 
// GetMySubShell / SetMySubShell: simulate old behavior,
// so that there is only one SubShell (only within the 5 own SubShells)
 
SfxShell* ScTabViewShell::GetMySubShell() const
{
    //  GetSubShell() was const before, and GetSubShell(sal_uInt16) should also be const...
 
    sal_uInt16 nPos = 0;
    SfxShell* pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(nPos);
    while (pSub)
    {
        if  (pSub == pDrawShell.get()  || pSub == pDrawTextShell.get() || pSub == pEditShell.get() ||
             pSub == pPivotShell.get() || pSub == pAuditingShell.get() || pSub == pDrawFormShell.get() ||
             pSub == pCellShell.get()  || pSub == pOleObjectShell.get() || pSub == pChartShell.get() ||
             pSub == pGraphicShell.get() || pSub == pMediaShell.get() || pSub == pPageBreakShell.get() ||
             pSub == m_pSparklineShell.get())
        {
            return pSub;    // found
        }
 
        pSub = const_cast<ScTabViewShell*>(this)->GetSubShell(++nPos);
    }
    return nullptr;        // none from mine present
}
 
bool ScTabViewShell::IsDrawTextShell() const
{
    return ( pDrawTextShell && ( GetMySubShell() == pDrawTextShell.get() ) );
}
 
bool ScTabViewShell::IsAuditShell() const
{
    return ( pAuditingShell && ( GetMySubShell() == pAuditingShell.get() ) );
}
 
void ScTabViewShell::SetDrawTextUndo( SfxUndoManager* pNewUndoMgr )
{
    // Default: undo manager for DocShell
    if (!pNewUndoMgr)
        pNewUndoMgr = GetViewData().GetDocShell()->GetUndoManager();
 
    if (pDrawTextShell)
    {
        pDrawTextShell->SetUndoManager(pNewUndoMgr);
        ScDocShell* pDocSh = GetViewData().GetDocShell();
        if ( pNewUndoMgr == pDocSh->GetUndoManager() &&
             !pDocSh->GetDocument().IsUndoEnabled() )
        {
            pNewUndoMgr->SetMaxUndoActionCount( 0 );
        }
    }
    else
    {
        OSL_FAIL("SetDrawTextUndo without DrawTextShell");
    }
}
 
ScTabViewShell* ScTabViewShell::GetActiveViewShell()
{
    return dynamic_cast< ScTabViewShell *>( SfxViewShell::Current() );
}
 
SfxPrinter* ScTabViewShell::GetPrinter( bool bCreate )
{
    // printer is always present (is created for the FontList already on start-up)
    return GetViewData().GetDocShell()->GetPrinter(bCreate);
}
 
sal_uInt16 ScTabViewShell::SetPrinter( SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
{
    return GetViewData().GetDocShell()->SetPrinter( pNewPrinter, nDiffFlags );
}
 
bool ScTabViewShell::HasPrintOptionsPage() const
{
    return true;
}
 
std::unique_ptr<SfxTabPage> ScTabViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions )
{
    ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
    ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
    if ( ScTpPrintOptionsCreate )
        return ScTpPrintOptionsCreate(pPage, pController, &rOptions);
    return nullptr;
}
 
void ScTabViewShell::StopEditShell()
{
    if ( pEditShell != nullptr && !bDontSwitch )
        SetEditShell(nullptr, false );
}
 
// close handler to ensure function of dialog:
 
IMPL_LINK_NOARG(ScTabViewShell, SimpleRefClose, const OUString*, void)
{
    SfxInPlaceClient* pClient = GetIPClient();
    if ( pClient && pClient->IsObjectInPlaceActive() )
    {
        // If range selection was started with an active embedded object,
        // switch back to original sheet (while the dialog is still open).
 
        SetTabNo( GetViewData().GetRefTabNo() );
    }
 
    ScSimpleRefDlgWrapper::SetAutoReOpen( true );
}
 
// handlers to call UNO listeners:
 
static ScTabViewObj* lcl_GetViewObj( const ScTabViewShell& rShell )
{
    ScTabViewObj* pRet = nullptr;
    SfxViewFrame& rViewFrame = rShell.GetViewFrame();
    SfxFrame& rFrame = rViewFrame.GetFrame();
    uno::Reference<frame::XController> xController = rFrame.GetController();
    if (xController.is())
        pRet = dynamic_cast<ScTabViewObj*>( xController.get() );
    return pRet;
}
 
IMPL_LINK( ScTabViewShell, SimpleRefDone, const OUString&, aResult, void )
{
    ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
    if ( pImpObj )
        pImpObj->RangeSelDone( aResult );
}
 
IMPL_LINK( ScTabViewShell, SimpleRefAborted, const OUString&, rResult, void )
{
    ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
    if ( pImpObj )
        pImpObj->RangeSelAborted( rResult );
}
 
IMPL_LINK( ScTabViewShell, SimpleRefChange, const OUString&, rResult, void )
{
    ScTabViewObj* pImpObj = lcl_GetViewObj( *this );
    if ( pImpObj )
        pImpObj->RangeSelChanged( rResult );
}
 
void ScTabViewShell::StartSimpleRefDialog(
            const OUString& rTitle, const OUString& rInitVal,
            bool bCloseOnButtonUp, bool bSingleCell, bool bMultiSelection )
{
    SfxViewFrame& rViewFrm = GetViewFrame();
 
    if ( GetActiveViewShell() != this )
    {
        // #i18833# / #i34499# The API method can be called for a view that's not active.
        // Then the view has to be activated first, the same way as in Execute for SID_CURRENTDOC.
        // Can't use GrabFocus here, because it needs to take effect immediately.
 
        rViewFrm.GetFrame().Appear();
    }
 
    sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
 
    SC_MOD()->SetRefDialog( nId, true, &rViewFrm );
 
    ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
    if (!pWnd)
        return;
 
    pWnd->SetCloseHdl( LINK( this, ScTabViewShell, SimpleRefClose ) );
    pWnd->SetUnoLinks( LINK( this, ScTabViewShell, SimpleRefDone ),
                       LINK( this, ScTabViewShell, SimpleRefAborted ),
                       LINK( this, ScTabViewShell, SimpleRefChange ) );
    pWnd->SetRefString( rInitVal );
    pWnd->SetFlags( bCloseOnButtonUp, bSingleCell, bMultiSelection );
    ScSimpleRefDlgWrapper::SetAutoReOpen( false );
    if (auto xWin = pWnd->GetController())
        xWin->set_title(rTitle);
    pWnd->StartRefInput();
}
 
void ScTabViewShell::StopSimpleRefDialog()
{
    SfxViewFrame& rViewFrm = GetViewFrame();
    sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
 
    ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(rViewFrm.GetChildWindow( nId ));
    if (pWnd)
    {
        if (auto pWin = pWnd->GetController())
            pWin->response(RET_CLOSE);
    }
}
 
bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt)
{
    ScModule* pScMod = SC_MOD();
 
    SfxViewFrame& rThisFrame = GetViewFrame();
    if ( rThisFrame.GetChildWindow( SID_OPENDLG_FUNCTION ) )
        return false;
 
    vcl::KeyCode aCode = rKEvt.GetKeyCode();
    bool bShift     = aCode.IsShift();
    bool bControl   = aCode.IsMod1();
    bool bAlt       = aCode.IsMod2();
    sal_uInt16 nCode    = aCode.GetCode();
    bool bUsed      = false;
    bool bInPlace   = pScMod->IsEditMode();     // Editengine gets all
    bool bAnyEdit   = pScMod->IsInputMode();    // only characters & backspace
    bool bDraw      = IsDrawTextEdit();
 
    HideNoteMarker();   // note marker
 
    // don't do extra HideCursor/ShowCursor calls if EnterHandler will switch to a different sheet
    bool bOnRefSheet = ( GetViewData().GetRefTabNo() == GetViewData().GetTabNo() );
    bool bHideCursor = ( ( nCode == KEY_RETURN && bInPlace ) || nCode == KEY_TAB ) && bOnRefSheet;
 
    if (bHideCursor)
        HideAllCursors();
 
    ScDocument& rDoc = GetViewData().GetDocument();
    rDoc.KeyInput();    // TimerDelays etc.
 
    if( bInPlace )
    {
        bUsed = pScMod->InputKeyEvent( rKEvt );         // input
        if( !bUsed )
            bUsed = SfxViewShell::KeyInput( rKEvt );    // accelerators
    }
    else if( bAnyEdit )
    {
        bool bIsType = false;
        sal_uInt16 nModi = aCode.GetModifier();
        sal_uInt16 nGroup = aCode.GetGroup();
 
        if ( nGroup == KEYGROUP_NUM || nGroup == KEYGROUP_ALPHA || nGroup == 0 )
            if ( !bControl && !bAlt )
                bIsType = true;
 
        if ( nGroup == KEYGROUP_MISC )
            switch ( nCode )
            {
                case KEY_RETURN:
                    bIsType = bControl && !bAlt;        // Control, Shift-Control-Return
                    if ( !bIsType && nModi == 0 )
                    {
                        // Does the Input Handler also want a simple Return?
 
                        ScInputHandler* pHdl = pScMod->GetInputHdl(this);
                        bIsType = pHdl && pHdl->TakesReturn();
                    }
                    break;
                case KEY_SPACE:
                    bIsType = !bControl && !bAlt;       // without modifier or Shift-Space
                    break;
                case KEY_ESCAPE:
                    bIsType = (nModi == 0); // only without modifier
                    break;
                default:
                    bIsType = true;
            }
        else if (nCode == KEY_RIGHT && !bControl && !bShift && !bAlt)
        {
            ScInputHandler* pHdl = pScMod->GetInputHdl(this);
            bIsType = pHdl && pHdl->HasPartialComplete();
        }
 
        if( bIsType )
            bUsed = pScMod->InputKeyEvent( rKEvt );     // input
 
        if( !bUsed )
            bUsed = SfxViewShell::KeyInput( rKEvt );    // accelerators
 
        if ( !bUsed && !bIsType && nCode != KEY_RETURN )    // input once again afterwards
            bUsed = pScMod->InputKeyEvent( rKEvt );
    }
    else
    {
        // special case: copy/cut for multiselect  -> error message
        //  (Slot is disabled, so SfxViewShell::KeyInput would be swallowed without a comment)
        KeyFuncType eFunc = aCode.GetFunction();
        if ( eFunc == KeyFuncType::CUT )
        {
            ScRange aDummy;
            ScMarkType eMarkType = GetViewData().GetSimpleArea( aDummy );
            if (eMarkType != SC_MARK_SIMPLE)
            {
                ErrorMessage(STR_NOMULTISELECT);
                bUsed = true;
            }
        }
        if (!bUsed)
            bUsed = SfxViewShell::KeyInput( rKEvt );    // accelerators
 
        //  during inplace editing, some slots are handled by the
        //  container app and are executed during Window::KeyInput.
        //  -> don't pass keys to input handler that would be used there
        //  but should call slots instead.
        bool bParent = ( GetViewFrame().GetFrame().IsInPlace() && eFunc != KeyFuncType::DONTKNOW );
 
        if( !bUsed && !bDraw && nCode != KEY_RETURN && !bParent )
            bUsed = pScMod->InputKeyEvent( rKEvt, true );       // input
    }
 
    if (!bInPlace && !bUsed && !bDraw)
    {
        switch (nCode)
        {
            case KEY_RETURN:
                {
                    bool bNormal = !bControl && !bAlt;
                    if ( !bAnyEdit && bNormal )
                    {
                        // Depending on options, Enter switches to edit mode.
                        const ScInputOptions& rOpt = pScMod->GetInputOptions();
                        if ( rOpt.GetEnterEdit() )
                        {
                            pScMod->SetInputMode( SC_INPUT_TABLE );
                            bUsed = true;
                        }
                    }
 
                    bool bEditReturn = bControl && !bShift;         // pass on to edit engine
                    if ( !bUsed && !bEditReturn )
                    {
                        if ( bOnRefSheet )
                            HideAllCursors();
 
                        ScEnterMode nMode = ScEnterMode::NORMAL;
                        if ( bShift && bControl )
                            nMode = ScEnterMode::MATRIX;
                        else if ( bAlt )
                            nMode = ScEnterMode::BLOCK;
                        pScMod->InputEnterHandler(nMode);
 
                        if (nMode == ScEnterMode::NORMAL)
                        {
                            if( bShift )
                                GetViewData().GetDispatcher().Execute( SID_CURSORENTERUP,
                                            SfxCallMode::SLOT | SfxCallMode::RECORD );
                            else
                                GetViewData().GetDispatcher().Execute( SID_CURSORENTERDOWN,
                                            SfxCallMode::SLOT | SfxCallMode::RECORD );
                        }
                        else
                            UpdateInputHandler(true);
 
                        if ( bOnRefSheet )
                            ShowAllCursors();
 
                        // here no UpdateInputHandler, since during reference input on another
                        // document this ViewShell is not the one that is used for input.
 
                        bUsed = true;
                    }
                }
                break;
        }
    }
 
    // hard-code Alt-Cursor key, since Alt is not configurable
 
    if ( !bUsed && bAlt && !bControl )
    {
        sal_uInt16 nSlotId = 0;
        switch (nCode)
        {
            case KEY_UP:
                ModifyCellSize( DIR_TOP, bShift );
                bUsed = true;
                break;
            case KEY_DOWN:
                ModifyCellSize( DIR_BOTTOM, bShift );
                bUsed = true;
                break;
            case KEY_LEFT:
                ModifyCellSize( DIR_LEFT, bShift );
                bUsed = true;
                break;
            case KEY_RIGHT:
                ModifyCellSize( DIR_RIGHT, bShift );
                bUsed = true;
                break;
            case KEY_PAGEUP:
                nSlotId = bShift ? SID_CURSORPAGELEFT_SEL : SID_CURSORPAGELEFT_;
                break;
            case KEY_PAGEDOWN:
                nSlotId = bShift ? SID_CURSORPAGERIGHT_SEL : SID_CURSORPAGERIGHT_;
                break;
            case KEY_EQUAL:
            {
                // #tdf39302: Use "Alt + =" for autosum
                if ( !bAnyEdit ) // Ignore shortcut if currently editing a cell
                {
                    ScInputHandler* pHdl = pScMod->GetInputHdl(this);
                    if ( pHdl )
                    {
                        ScInputWindow* pWin = pHdl->GetInputWindow();
                        if ( pWin )
                        {
                            bool bRangeFinder = false;
                            bool bSubTotal = false;
                            pWin->AutoSum( bRangeFinder, bSubTotal, ocSum );
                        }
                    }
 
                    bUsed = true;
                    break;
                }
            }
        }
        if ( nSlotId )
        {
            GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
            bUsed = true;
        }
    }
 
    // use Ctrl+Alt+Shift+arrow keys to move the cursor in cells
    // while keeping the last selection
    if ( !bUsed && bAlt && bControl && bShift)
    {
        sal_uInt16 nSlotId = 0;
        switch (nCode)
        {
            case KEY_UP:
                nSlotId = SID_CURSORUP;
                break;
            case KEY_DOWN:
                nSlotId = SID_CURSORDOWN;
                break;
            case KEY_LEFT:
                nSlotId = SID_CURSORLEFT;
                break;
            case KEY_RIGHT:
                nSlotId = SID_CURSORRIGHT;
                break;
            case KEY_PAGEUP:
                nSlotId = SID_CURSORPAGEUP;
                break;
            case KEY_PAGEDOWN:
                nSlotId = SID_CURSORPAGEDOWN;
                break;
            case KEY_HOME:
                nSlotId = SID_CURSORHOME;
                break;
            case KEY_END:
                nSlotId = SID_CURSOREND;
                break;
            default:
                nSlotId = 0;
                break;
        }
        if ( nSlotId )
        {
            sal_uInt16 nMode = GetLockedModifiers();
            LockModifiers(KEY_MOD1);
            GetViewData().GetDispatcher().Execute( nSlotId, SfxCallMode::SLOT | SfxCallMode::RECORD );
            LockModifiers(nMode);
            bUsed = true;
        }
    }
    if (bHideCursor)
        ShowAllCursors();
 
    return bUsed;
}
 
bool ScTabViewShell::SfxKeyInput(const KeyEvent& rKeyEvent)
{
    return SfxViewShell::KeyInput( rKeyEvent );
}
 
bool ScTabViewShell::KeyInput( const KeyEvent &rKeyEvent )
{
    return TabKeyInput( rKeyEvent );
}
 
void ScTabViewShell::Construct( TriState nForceDesignMode )
{
    SfxApplication* pSfxApp  = SfxGetpApp();
    ScDocShell* pDocSh = GetViewData().GetDocShell();
    ScDocument& rDoc = pDocSh->GetDocument();
    bReadOnly = pDocSh->IsReadOnly();
    bIsActive = false;
 
    EnableAutoSpell(ScModule::GetAutoSpellProperty());
 
    SetName(u"View"_ustr); // for SBX
    Color aColBlack( COL_BLACK );
    SetPool( &SC_MOD()->GetPool() );
    SetWindow( GetActiveWin() );
 
    pCurFrameLine.reset( new ::editeng::SvxBorderLine(&aColBlack, 20, SvxBorderLineStyle::SOLID) );
    StartListening(*GetViewData().GetDocShell(), DuplicateHandling::Prevent);
    StartListening(GetViewFrame(), DuplicateHandling::Prevent);
    StartListening(*pSfxApp, DuplicateHandling::Prevent); // #i62045# #i62046# application is needed for Calc's own hints
 
    SfxViewFrame* pFirst = SfxViewFrame::GetFirst(pDocSh);
    bool bFirstView = !pFirst
          || (pFirst == &GetViewFrame() && !SfxViewFrame::GetNext(*pFirst,pDocSh));
 
    if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        //TODO/LATER: is there a difference between the two GetVisArea methods?
        tools::Rectangle aVisArea = static_cast<const SfxObjectShell*>(pDocSh)->GetVisArea();
 
        SCTAB nVisTab = rDoc.GetVisibleTab();
        if (!rDoc.HasTable(nVisTab))
        {
            nVisTab = 0;
            rDoc.SetVisibleTab(nVisTab);
        }
        SetTabNo( nVisTab );
        bool bNegativePage = rDoc.IsNegativePage( nVisTab );
        // show the right cells
        GetViewData().SetScreenPos( bNegativePage ? aVisArea.TopRight() : aVisArea.TopLeft() );
 
        if ( GetViewFrame().GetFrame().IsInPlace() )                         // inplace
        {
            pDocSh->SetInplace( true );             // already initiated like this
            if (rDoc.IsEmbedded())
                rDoc.ResetEmbedded();              // no blue mark
        }
        else if ( bFirstView )
        {
            pDocSh->SetInplace( false );
            GetViewData().RefreshZoom();           // recalculate PPT
            if (!rDoc.IsEmbedded())
                rDoc.SetEmbedded( rDoc.GetVisibleTab(), aVisArea );                  // mark VisArea
        }
    }
 
    // ViewInputHandler
    // Each task now has its own InputWindow,
    // therefore either should each task get its own InputHandler,
    // or the InputWindow should create its own InputHandler
    // (then always search via InputWindow and only if not found
    // use the InputHandler of the App).
    // As an intermediate solution each View gets its own InputHandler,
    // which only yields problems if two Views are in one task window.
    mpInputHandler.reset(new ScInputHandler);
 
    // old version:
    //  if ( !GetViewFrame().ISA(SfxTopViewFrame) )        // OLE or Plug-In
    //      pInputHandler = new ScInputHandler;
 
            // create FormShell before MakeDrawView, so that DrawView can be registered at the
            // FormShell in every case
            // the FormShell is pushed in the first activate
    pFormShell.reset( new FmFormShell(this) );
    pFormShell->SetControlActivationHandler( LINK( this, ScTabViewShell, FormControlActivated ) );
 
            // DrawView must not be created in TabView - ctor,
            // if the ViewShell is not yet constructed...
    if (rDoc.GetDrawLayer())
        MakeDrawView( nForceDesignMode );
    ViewOptionsHasChanged(false, false);   // possibly also creates DrawView
 
    SfxUndoManager* pMgr = pDocSh->GetUndoManager();
    SetUndoManager( pMgr );
    pFormShell->SetUndoManager( pMgr );
    if ( !rDoc.IsUndoEnabled() )
    {
        pMgr->SetMaxUndoActionCount( 0 );
    }
    SetRepeatTarget( &aTarget );
    pFormShell->SetRepeatTarget( &aTarget );
 
    if ( bFirstView )   // first view?
    {
        rDoc.SetDocVisible( true );        // used when creating new sheets
        if ( pDocSh->IsEmpty() )
        {
            // set first sheet's RTL flag (following will already be initialized because of SetDocVisible)
            rDoc.SetLayoutRTL( 0, ScGlobal::IsSystemRTL() );
 
            // append additional sheets (not for OLE object)
            if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
            {
                // Get the customized initial tab count
                const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
                SCTAB nInitTabCount = rOpt.GetInitTabCount();
 
                for (SCTAB i=1; i<nInitTabCount; i++)
                    rDoc.MakeTable(i,false);
            }
 
            pDocSh->SetEmpty( false );          // #i6232# make sure this is done only once
        }
 
        // ReadExtOptions is now in Activate
 
        // link update no nesting
        if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::INTERNAL &&
             pDocSh->IsUpdateEnabled() )  // #105575#; update only in the first creation of the ViewShell
        {
            // Check if there are any external data.
            bool bLink = rDoc.GetExternalRefManager()->hasExternalData();
            if (!bLink)
            {
                // #i100042# sheet links can still exist independently from external formula references
                SCTAB nTabCount = rDoc.GetTableCount();
                for (SCTAB i=0; i<nTabCount && !bLink; i++)
                    if (rDoc.IsLinked(i))
                        bLink = true;
            }
            if (!bLink)
            {
                const sc::DocumentLinkManager& rMgr = rDoc.GetDocLinkManager();
                if (rDoc.HasLinkFormulaNeedingCheck() || rDoc.HasAreaLinks() || rMgr.hasDdeOrOleOrWebServiceLinks())
                    bLink = true;
            }
            if (bLink)
            {
                if ( !pFirst )
                    pFirst = &GetViewFrame();
 
                if(SC_MOD()->GetCurRefDlgId()==0)
                {
                        pFirst->GetDispatcher()->Execute( SID_UPDATETABLINKS,
                                            SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
                }
            }
            else
            {
                // No links yet, but loading an existing document may have
                // disabled link update but there's no "Allow updating" infobar
                // that could enable it again. So in order to enable the user
                // to add formulas with external references allow link updates
                // again.
                pDocSh->AllowLinkUpdate();
            }
 
            bool bReImport = false;                             // update imported data
            ScDBCollection* pDBColl = rDoc.GetDBCollection();
            if ( pDBColl )
            {
                const ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
                bReImport = std::any_of(rDBs.begin(), rDBs.end(),
                    [](const std::unique_ptr<ScDBData>& rxDB) { return rxDB->IsStripData() && rxDB->HasImportParam() && !rxDB->HasImportSelection(); });
            }
            if (bReImport)
            {
                if ( !pFirst )
                    pFirst = &GetViewFrame();
                if(SC_MOD()->GetCurRefDlgId()==0)
                {
                    pFirst->GetDispatcher()->Execute( SID_REIMPORT_AFTER_LOAD,
                                            SfxCallMode::ASYNCHRON | SfxCallMode::RECORD );
                }
            }
        }
    }
 
    UpdateAutoFillMark();
 
    // ScDispatchProviderInterceptor registers itself in ctor
    xDisProvInterceptor = new ScDispatchProviderInterceptor( this );
 
    bFirstActivate = true; // delay NavigatorUpdate until Activate()
 
    // #105575#; update only in the first creation of the ViewShell
    pDocSh->SetUpdateEnabled(false);
 
    if ( GetViewFrame().GetFrame().IsInPlace() )
        UpdateHeaderWidth(); // The inplace activation requires headers to be calculated
 
    SvBorder aBorder;
    GetBorderSize( aBorder, Size() );
    SetBorderPixel( aBorder );
}
 
class ScViewOptiChangesListener : public cppu::WeakImplHelper<util::XChangesListener>
{
public:
    ScViewOptiChangesListener(ScTabViewShell&);
    void stopListening();
 
    virtual void SAL_CALL changesOccurred(const util::ChangesEvent& Event) override;
    virtual void SAL_CALL disposing(const lang::EventObject& rEvent) override;
 
private:
    ScTabViewShell& mrViewShell;
    uno::Reference<util::XChangesNotifier> m_xViewChangesNotifier;
    uno::Reference<util::XChangesNotifier> m_xColorSchemeChangesNotifier;
};
 
void ScViewOptiChangesListener::stopListening()
{
    if (m_xViewChangesNotifier)
        m_xViewChangesNotifier->removeChangesListener(this);
    if (m_xColorSchemeChangesNotifier)
        m_xColorSchemeChangesNotifier->removeChangesListener(this);
}
 
// virtual
void SAL_CALL ScViewOptiChangesListener::changesOccurred(const util::ChangesEvent& rEvent)
{
    for (const auto& change : rEvent.Changes)
    {
        if (OUString sChangedEntry;
            (change.Accessor >>= sChangedEntry) && sChangedEntry == "ColumnRowHighlighting")
        {
            mrViewShell.HighlightOverlay();
            break;
        }
 
        if (OUString sChangedEntry; (change.Accessor >>= sChangedEntry) && sChangedEntry ==
            "ColorSchemes/org.openoffice.Office.UI:ColorScheme['COLOR_SCHEME_LIBREOFFICE_AUTOMATIC']/CalcCellFocus/Color")
        {
            mrViewShell.GetActiveWin()->UpdateCursorOverlay();
            mrViewShell.GetActiveWin()->UpdateAutoFillOverlay();
            mrViewShell.GetActiveWin()->UpdateHighlightOverlay();
            break;
        }
    }
}
 
// virtual
void SAL_CALL ScViewOptiChangesListener::disposing(const lang::EventObject& /* rEvent */)
{
    m_xViewChangesNotifier.clear();
    m_xColorSchemeChangesNotifier.clear();
}
 
ScViewOptiChangesListener::ScViewOptiChangesListener(ScTabViewShell& rViewShell)
    : mrViewShell(rViewShell)
{
    // add a listener for configuration changes (ColumnRowHighlighting Checkbox)
    uno::Reference<lang::XMultiServiceFactory> xConfigurationProvider(
        configuration::theDefaultProvider::get(comphelper::getProcessComponentContext()));
 
    beans::NamedValue aViewProperty{ u"nodepath"_ustr,
                                 uno::Any(u"/org.openoffice.Office.Calc/Content/Display"_ustr) };
 
    beans::NamedValue aColorSchemeProperty{ u"nodepath"_ustr,
                                 uno::Any(u"/org.openoffice.Office.UI/ColorScheme"_ustr) };
 
    uno::Reference<uno::XInterface> xViewConfigurationAccess
        = xConfigurationProvider->createInstanceWithArguments(
            u"com.sun.star.configuration.ConfigurationAccess"_ustr, { uno::Any(aViewProperty) });
 
    uno::Reference<uno::XInterface> xColorSchemeConfigurationAccess
        = xConfigurationProvider->createInstanceWithArguments(
            u"com.sun.star.configuration.ConfigurationAccess"_ustr, { uno::Any(aColorSchemeProperty) });
 
    m_xViewChangesNotifier.set(xViewConfigurationAccess, uno::UNO_QUERY);
    m_xColorSchemeChangesNotifier.set(xColorSchemeConfigurationAccess, uno::UNO_QUERY);
 
    if (m_xViewChangesNotifier)
        m_xViewChangesNotifier->addChangesListener(this);
 
    if (m_xColorSchemeChangesNotifier)
        m_xColorSchemeChangesNotifier->addChangesListener(this);
}
 
static void lcl_RemoveCells(uno::Reference<sheet::XSpreadsheet>& rSheet, sal_uInt16 nSheet,
                     sal_uInt32 nStartColumn, sal_uInt32 nStartRow, sal_uInt32 nEndColumn,
                     sal_uInt32 nEndRow, bool bRows)
{
    table::CellRangeAddress aCellRange(nSheet, nStartColumn, nStartRow, nEndColumn, nEndRow);
    uno::Reference<sheet::XCellRangeMovement> xCRM(rSheet, uno::UNO_QUERY);
 
    if (xCRM.is())
    {
        if (bRows)
            xCRM->removeRange(aCellRange, sheet::CellDeleteMode_UP);
        else
            xCRM->removeRange(aCellRange, sheet::CellDeleteMode_LEFT);
    }
}
 
/*  For rows (bool bRows), I am passing reference to already existing sequence, and comparing the required
 *  columns, whereas for columns, I am creating a sequence for each, with only the checked entries
 *  in the dialog.
 */
static bool lcl_CheckInArray(std::vector<uno::Sequence<uno::Any>>& nUniqueRecords,
                             const uno::Sequence<uno::Any>& nCurrentRecord,
                             const std::vector<int>& rSelectedEntries, bool bRows)
{
    for (size_t m = 0; m < nUniqueRecords.size(); ++m)
    {
        bool bIsDuplicate = true;
        for (size_t n = 0; n < rSelectedEntries.size(); ++n)
        {
            // when the first different element is found
            int nColumn = (bRows ? rSelectedEntries[n] : n);
            if (nUniqueRecords[m][nColumn] != (bRows ? nCurrentRecord[rSelectedEntries[n]] : nCurrentRecord[n]))
            {
                bIsDuplicate = false;
                break;
            }
        }
 
        if (bIsDuplicate)
            return true;
    }
    return false;
}
 
uno::Reference<css::sheet::XSpreadsheet> ScTabViewShell::GetRangeWithSheet(css::table::CellRangeAddress& rRangeData, bool& bHasData, bool bHasUnoArguments)
{
    // get spreadsheet document model & controller
    uno::Reference<frame::XModel> xModel(GetViewData().GetDocShell()->GetModel());
    uno::Reference<frame::XController> xController(xModel->getCurrentController());
 
    // spreadsheet's extension of com.sun.star.frame.Controller service
    uno::Reference<sheet::XSpreadsheetView> SpreadsheetDocument(xController, uno::UNO_QUERY);
    uno::Reference<sheet::XSpreadsheet> ActiveSheet = SpreadsheetDocument->getActiveSheet();
 
    if (!bHasUnoArguments)
    {
        // get the selection supplier, extract selection in XSheetCellRange
        uno::Reference<view::XSelectionSupplier> xSelectionSupplier(SpreadsheetDocument, uno::UNO_QUERY);
        uno::Any Selection = xSelectionSupplier->getSelection();
        uno::Reference<sheet::XSheetCellRange> SelectedCellRange;
        Selection >>= SelectedCellRange;
 
        // Get the Selected Range Address.
        uno::Reference<sheet::XCellRangeAddressable> xAddressable( SelectedCellRange, uno::UNO_QUERY);
        if (xAddressable.is())
            rRangeData = xAddressable->getRangeAddress();
        else
        {
            bHasData = false;
            return ActiveSheet;
        }
    }
 
    SCCOL nStartColumn = rRangeData.StartColumn;
    SCCOL nEndColumn = rRangeData.EndColumn;
    SCROW nStartRow = rRangeData.StartRow;
    SCROW nEndRow = rRangeData.EndRow;
 
    // shrink to intersection of data and selection. If no intersection ==> return
    bHasData = GetViewData().GetDocument().ShrinkToDataArea(rRangeData.Sheet, nStartColumn, nStartRow, nEndColumn, nEndRow);
 
    rRangeData.StartColumn = nStartColumn;
    rRangeData.StartRow = nStartRow;
    rRangeData.EndColumn = nEndColumn;
    rRangeData.EndRow = nEndRow;
 
    return ActiveSheet;
}
 
void ScTabViewShell::ExtendSingleSelection(css::table::CellRangeAddress& rRangeData)
{
    SCCOL aStartCol(rRangeData.StartColumn);
    SCCOL aEndCol(rRangeData.EndColumn);
    SCROW aStartRow(rRangeData.StartRow);
    SCROW aEndRow(rRangeData.EndRow);
 
    GetViewData().GetDocument().GetDataArea(rRangeData.Sheet, aStartCol, aStartRow, aEndCol,
                                            aEndRow, true, false);
    MarkRange(ScRange(ScAddress(aStartCol, aStartRow, rRangeData.Sheet),
                      ScAddress(aEndCol, aEndRow, rRangeData.Sheet)));
 
    rRangeData.StartRow = aStartRow;
    rRangeData.StartColumn = aStartCol;
    rRangeData.EndRow = aEndRow;
    rRangeData.EndColumn = aEndCol;
}
 
/* bool bRemove == false ==> highlight duplicate rows */
void ScTabViewShell::HandleDuplicateRecords(css::uno::Reference<css::sheet::XSpreadsheet> ActiveSheet,
                                const css::table::CellRangeAddress& aRange, bool bRemove,
                                bool bIncludesHeaders, bool bDuplicateRows,
                                const std::vector<int>& rSelectedEntries)
{
    if (rSelectedEntries.size() == 0)
    {
        Unmark();
        return;
    }
 
    uno::Reference<frame::XModel> xModel(GetViewData().GetDocShell()->GetModel());
    uno::Reference<sheet::XSheetCellRange> xSheetRange(
            ActiveSheet->getCellRangeByPosition(aRange.StartColumn, aRange.StartRow, aRange.EndColumn, aRange.EndRow),
            uno::UNO_QUERY);
 
 
    uno::Reference<sheet::XCellRangeData> xCellRangeData(xSheetRange, uno::UNO_QUERY);
    uno::Sequence<uno::Sequence<uno::Any>> aDataArray = xCellRangeData->getDataArray();
 
    uno::Reference< document::XUndoManagerSupplier > xUndoManager( xModel, uno::UNO_QUERY );
    uno::Reference<document::XActionLockable> xLockable(xModel, uno::UNO_QUERY);
 
    uno::Reference<sheet::XCalculatable> xCalculatable(xModel, uno::UNO_QUERY);
    bool bAutoCalc = xCalculatable->isAutomaticCalculationEnabled();
 
    comphelper::ScopeGuard aUndoContextGuard(
        [&xUndoManager, &xLockable, &xModel, &xCalculatable, &bAutoCalc, &bRemove] {
        xUndoManager->getUndoManager()->leaveUndoContext();
        if (bRemove)
            xCalculatable->enableAutomaticCalculation(bAutoCalc);
        xLockable->removeActionLock();
        if (xModel->hasControllersLocked())
            xModel->unlockControllers();
    });
 
    xModel->lockControllers();
    xLockable->addActionLock();
    if (bRemove)
        xCalculatable->enableAutomaticCalculation(true);
    xUndoManager->getUndoManager()->enterUndoContext("HandleDuplicateRecords");
 
    bool nModifier = false;         // modifier key pressed?
    bool bNoDuplicatesForSelection = true;
 
    if (bDuplicateRows)
    {
        std::vector<uno::Sequence<uno::Any>> aUnionArray;
        sal_uInt32 nRow = bIncludesHeaders ? 1 : 0;
        sal_uInt32 lRows = aDataArray.getLength();
        sal_uInt32 nDeleteCount = 0;
 
        while (nRow < lRows)
        {
            if (lcl_CheckInArray(aUnionArray, aDataArray[nRow], rSelectedEntries, true))
            {
                if (bRemove)
                {
                    lcl_RemoveCells(ActiveSheet, aRange.Sheet, aRange.StartColumn,
                                    aRange.StartRow + nRow - nDeleteCount, aRange.EndColumn,
                                    aRange.StartRow + nRow - nDeleteCount, true);
                    ++nDeleteCount;
                }
                else
                {
                    for (int nCol = aRange.StartColumn; nCol <= aRange.EndColumn; ++nCol)
                    {
                        bNoDuplicatesForSelection = false;
                        DoneBlockMode( nModifier );
                        nModifier = true;
                        InitBlockMode( nCol, aRange.StartRow + nRow, aRange.Sheet, false, false);
                    }
                }
            }
            else
            {
                aUnionArray.push_back(aDataArray[nRow]);
            }
            ++nRow;
        }
    }
    else
    {
        std::vector<uno::Sequence<uno::Any>> aUnionArray;
        sal_uInt32 nDeleteCount = 0;
        sal_uInt32 nColumn = bIncludesHeaders ? 1 : 0;
        sal_uInt32 lColumns = aDataArray[0].getLength();
 
        while (nColumn < lColumns)
        {
            uno::Sequence<uno::Any> aSeq;
            aSeq.realloc(rSelectedEntries.size());
            for (size_t i = 0; i < rSelectedEntries.size(); ++i)
                aSeq.getArray()[i] = aDataArray[rSelectedEntries[i]][nColumn];
 
            if (lcl_CheckInArray(aUnionArray, aSeq, rSelectedEntries, false))
            {
                if (bRemove)
                {
                    lcl_RemoveCells(ActiveSheet, aRange.Sheet,
                                    aRange.StartColumn + nColumn - nDeleteCount, aRange.StartRow,
                                    aRange.StartColumn + nColumn - nDeleteCount, aRange.EndRow, false);
                    ++nDeleteCount;
                }
                else
                {
                    for (int nRow = aRange.StartRow; nRow <= aRange.EndRow; ++nRow)
                    {
                        bNoDuplicatesForSelection = false;
                        DoneBlockMode( nModifier );
                        nModifier = true;
                        InitBlockMode( aRange.StartColumn + nColumn, nRow, aRange.Sheet, false, false);
                    }
                }
            }
            else
            {
                aUnionArray.push_back(aSeq);
            }
            ++nColumn;
        }
 
    }
 
    if (bNoDuplicatesForSelection && !bRemove)
        Unmark();
}
 
ScTabViewShell::ScTabViewShell( SfxViewFrame& rViewFrame,
                                SfxViewShell* pOldSh ) :
    SfxViewShell(rViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS),
    ScDBFunc( &rViewFrame.GetWindow(), static_cast<ScDocShell&>(*rViewFrame.GetObjectShell()), this ),
    eCurOST(OST_NONE),
    nDrawSfxId(0),
    aTarget(this),
    bActiveDrawSh(false),
    bActiveDrawTextSh(false),
    bActiveDrawFormSh(false),
    bActiveOleObjectSh(false),
    bActiveChartSh(false),
    bActiveGraphicSh(false),
    bActiveMediaSh(false),
    bFormShellAtTop(false),
    bDontSwitch(false),
    bInFormatDialog(false),
    bReadOnly(false),
    bForceFocusOnCurCell(false),
    bInPrepareClose(false),
    bInDispose(false),
    bMoveKeepEdit(false),
    nCurRefDlgId(0),
    mbInSwitch(false),
    m_pDragData(new ScDragData),
    m_pScCondFormatDlgItem()
{
    const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions();
 
    //  if switching back from print preview,
    //  restore the view settings that were active when creating the preview
    //  ReadUserData must not happen from ctor, because the view's edit window
    //  has to be shown by the sfx. ReadUserData is deferred until the first Activate call.
    //  old DesignMode state from form layer must be restored, too
 
    TriState nForceDesignMode = TRISTATE_INDET;
    if ( auto pPreviewShell = dynamic_cast<ScPreviewShell*>( pOldSh) )
    {
        nForceDesignMode = pPreviewShell->GetSourceDesignMode();
        ScPreview* p = pPreviewShell->GetPreview();
        if (p)
            GetViewData().GetMarkData().SetSelectedTabs(p->GetSelectedTabs());
    }
 
    Construct( nForceDesignMode );
 
    // make Controller known to SFX
    new ScTabViewObj( this );
 
    // Resolves: tdf#53899 if there is no controller, register the above
    // ScTabViewObj as the current controller for the duration of the first
    // round of calculations triggered here by SetZoom. That way any StarBasic
    // macros triggered while the document is loading have a CurrentController
    // available to them.
    bool bInstalledScTabViewObjAsTempController = false;
    uno::Reference<frame::XController> xCurrentController(GetViewData().GetDocShell()->GetModel()->getCurrentController());
    if (!xCurrentController)
    {
        //GetController here returns the ScTabViewObj above
        GetViewData().GetDocShell()->GetModel()->setCurrentController(GetController());
        bInstalledScTabViewObjAsTempController = true;
    }
    xCurrentController.clear();
 
    if ( GetViewData().GetDocShell()->IsPreview() )
    {
        //  preview for template dialog: always show whole page
        SetZoomType( SvxZoomType::WHOLEPAGE, true );    // zoom value is recalculated at next Resize
    }
    else
    {
        Fraction aFract( rAppOpt.GetZoom(), 100 );
        SetZoom( aFract, aFract, true );
        SetZoomType( rAppOpt.GetZoomType(), true );
    }
 
    SetCurSubShell(OST_Cell);
    SvBorder aBorder;
    GetBorderSize( aBorder, Size() );
    SetBorderPixel( aBorder );
 
    MakeDrawLayer();
 
    //put things back as we found them
    if (bInstalledScTabViewObjAsTempController)
        GetViewData().GetDocShell()->GetModel()->setCurrentController(nullptr);
 
    mChangesListener.set(new ScViewOptiChangesListener(*this));
 
    // formula mode in online is not usable in collaborative mode,
    // this is a workaround for disabling formula mode in online
    // when there is more than a single view
    if (!comphelper::LibreOfficeKit::isActive())
        return;
 
    SfxViewShell* pViewShell = SfxViewShell::GetFirst();
    // have we already one view ?
    if (!pViewShell)
        return;
 
    // this view is not yet visible at this stage, so we look for not visible views, too, for this same document
    SfxViewShell* pViewShell2 = pViewShell;
    do
    {
        pViewShell2 = SfxViewShell::GetNext(*pViewShell2, /*only visible shells*/ false);
    } while (pViewShell2 && pViewShell2->GetDocId() != pViewShell->GetDocId());
    // if the second view is not this one, it means that there is
    // already more than one active view and so the formula mode
    // has already been disabled
    if (pViewShell2 && pViewShell2 == this)
    {
        ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
        assert(pTabViewShell);
        ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
        if (pInputHdl && pInputHdl->IsFormulaMode())
        {
            pInputHdl->SetMode(SC_INPUT_NONE);
        }
    }
 
    if (comphelper::LibreOfficeKit::isActive())
    {
        ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(GetCurrentDocument());
        SfxLokHelper::notifyViewRenderState(this, pModel);
    }
}
 
ScTabViewShell::~ScTabViewShell()
{
    bInDispose = true;
    mChangesListener->stopListening();
    mChangesListener.clear();
 
    // Notify other LOK views that we are going away.
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"_ostr);
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""_ostr);
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"_ostr);
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY"_ostr);
 
    // all to NULL, in case the TabView-dtor tries to access them
    //! (should not really! ??!?!)
    if (mpInputHandler)
    {
        mpInputHandler->SetDocumentDisposing(true);
    }
 
    ScDocShell* pDocSh = GetViewData().GetDocShell();
    EndListening(*pDocSh);
    EndListening(GetViewFrame());
    EndListening(*SfxGetpApp());           // #i62045# #i62046# needed now - SfxViewShell no longer does it
 
    SC_MOD()->ViewShellGone(this);
 
    RemoveSubShell();           // all
    SetWindow(nullptr);
 
    // need kill editview or we will touch the editengine after it has been freed by the ScInputHandler
    KillEditView(true);
 
    pFontworkBarShell.reset();
    pExtrusionBarShell.reset();
    pCellShell.reset();
    pPageBreakShell.reset();
    pDrawShell.reset();
    pDrawFormShell.reset();
    pOleObjectShell.reset();
    pChartShell.reset();
    pGraphicShell.reset();
    pMediaShell.reset();
    pDrawTextShell.reset();
    pEditShell.reset();
    pPivotShell.reset();
    m_pSparklineShell.reset();
    pAuditingShell.reset();
    pCurFrameLine.reset();
    mpFormEditData.reset();
    mpInputHandler.reset();
    pDialogDPObject.reset();
    pNavSettings.reset();
 
    pFormShell.reset();
    pAccessibilityBroadcaster.reset();
}
 
void ScTabViewShell::SetDialogDPObject( std::unique_ptr<ScDPObject> pObj )
{
    pDialogDPObject = std::move(pObj);
}
 
void ScTabViewShell::FillFieldData( ScHeaderFieldData& rData )
{
    ScDocShell* pDocShell = GetViewData().GetDocShell();
    ScDocument& rDoc = pDocShell->GetDocument();
    SCTAB nTab = GetViewData().GetTabNo();
    OUString aTmp;
    rDoc.GetName(nTab, aTmp);
    rData.aTabName = aTmp;
 
    if( pDocShell->getDocProperties()->getTitle().getLength() != 0 )
        rData.aTitle = pDocShell->getDocProperties()->getTitle();
    else
        rData.aTitle = pDocShell->GetTitle();
 
    const INetURLObject& rURLObj = pDocShell->GetMedium()->GetURLObject();
    rData.aLongDocName  = rURLObj.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
    if ( !rData.aLongDocName.isEmpty() )
        rData.aShortDocName = rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous);
    else
        rData.aShortDocName = rData.aLongDocName = rData.aTitle;
    rData.nPageNo       = 1;
    rData.nTotalPages   = 99;
 
    // eNumType is known by the dialog
}
 
ScNavigatorSettings* ScTabViewShell::GetNavigatorSettings()
{
    if( !pNavSettings )
        pNavSettings.reset(new ScNavigatorSettings);
    return pNavSettings.get();
}
 
tools::Rectangle ScTabViewShell::getLOKVisibleArea() const
{
    return GetViewData().getLOKVisibleArea();
}
 
void ScTabViewShell::SetDragObject(ScTransferObj* pCellObj, ScDrawTransferObj* pDrawObj)
{
    ResetDragObject();
    m_pDragData->pCellTransfer = pCellObj;
    m_pDragData->pDrawTransfer = pDrawObj;
}
 
void ScTabViewShell::ResetDragObject()
{
    m_pDragData->pCellTransfer = nullptr;
    m_pDragData->pDrawTransfer = nullptr;
    m_pDragData->pJumpLocalDoc = nullptr;
    m_pDragData->aLinkDoc.clear();
    m_pDragData->aLinkTable.clear();
    m_pDragData->aLinkArea.clear();
    m_pDragData->aJumpTarget.clear();
    m_pDragData->aJumpText.clear();
}
 
void ScTabViewShell::SetDragLink(const OUString& rDoc, const OUString& rTab, const OUString& rArea)
{
    ResetDragObject();
    m_pDragData->aLinkDoc   = rDoc;
    m_pDragData->aLinkTable = rTab;
    m_pDragData->aLinkArea  = rArea;
}
 
void ScTabViewShell::SetDragJump(ScDocument* pLocalDoc, const OUString& rTarget, const OUString& rText)
{
    ResetDragObject();
    m_pDragData->pJumpLocalDoc = pLocalDoc;
    m_pDragData->aJumpTarget = rTarget;
    m_pDragData->aJumpText = rText;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V522 There might be dereferencing of a potential null pointer 'pTabViewShell'.