/* -*- 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 <sal/config.h>
 
#include <scitems.hxx>
#include <editeng/eeitem.hxx>
 
#include <sfx2/app.hxx>
#include <svx/dialogs.hrc>
#include <svx/extrusionbar.hxx>
#include <svx/fontworkbar.hxx>
#include <editeng/boxitem.hxx>
#include <svx/fmpage.hxx>
#include <svx/fmshell.hxx>
#include <editeng/sizeitem.hxx>
#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
#include <sfx2/request.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/dispatch.hxx>
#include <svl/whiter.hxx>
#include <unotools/moduleoptions.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/docfile.hxx>
#include <o3tl/make_unique.hxx>
 
#include <tabvwsh.hxx>
#include <sc.hrc>
#include <globstr.hrc>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <docsh.hxx>
#include <scmod.hxx>
#include <appoptio.hxx>
#include <rangeutl.hxx>
#include <printfun.hxx>
#include <drawsh.hxx>
#include <drformsh.hxx>
#include <editsh.hxx>
#include <pivotsh.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 <anyrefdg.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 <tpprint.hxx>
#include <scextopt.hxx>
#include <printopt.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 <docoptio.hxx>
#include <documentlinkmgr.hxx>
#include <gridwin.hxx>
 
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
#include <com/sun/star/chart2/XCoordinateSystem.hpp>
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
#include <com/sun/star/chart2/XChartType.hpp>
#include <sfx2/lokhelper.hxx>
#include <comphelper/flagguard.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx>
 
using namespace com::sun::star;
 
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* pThisFrame  = GetViewFrame();
        if ( mpInputHandler && pThisFrame->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 = pThisFrame->GetChildWindow(FID_INPUTLINE_STATUS);
            if (pChild)
            {
                ScInputWindow* pWin = static_cast<ScInputWindow*>(pChild->GetWindow());
                if (pWin && pWin->IsVisible())
                {
 
                    ScInputHandler* pOldHdl=pWin->GetInputHandler();
 
                    SfxViewShell* pSh = SfxViewShell::GetFirst( true, checkSfxViewShell<ScTabViewShell> );
                    while ( pSh!=nullptr && pOldHdl!=nullptr)
                    {
                        if (static_cast<ScTabViewShell*>(pSh)->GetInputHandler() == pOldHdl)
                        {
                            pOldHdl->ResetDelayTimer();
                            break;
                        }
                        pSh = SfxViewShell::GetNext( *pSh, true, checkSfxViewShell<ScTabViewShell> );
                    }
 
                    pWin->SetInputHandler( mpInputHandler.get() );
                }
            }
        }
 
        UpdateInputHandler( /*bForce=*/ true, /*bStopEditing=*/ !comphelper::LibreOfficeKit::isActive() );
 
        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 ( pThisFrame->HasChildWindow(FID_CHG_ACCEPT) )
        {
            SfxChildWindow* pChild = pThisFrame->GetChildWindow(FID_CHG_ACCEPT);
            if (pChild)
            {
                static_cast<ScAcceptChgDlgWrapper*>(pChild)->ReInitDlg();
            }
        }
 
        if(pScMod->IsRefDialogOpen())
        {
            sal_uInt16 nModRefDlgId=pScMod->GetCurRefDlgId();
            SfxChildWindow* pChildWnd = pThisFrame->GetChildWindow( nModRefDlgId );
            if ( pChildWnd )
            {
                IAnyRefDialog* pRefDlg = dynamic_cast<IAnyRefDialog*>(pChildWnd->GetWindow());
                assert(pRefDlg);
                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)
 
    ContextChangeEventMultiplexer::NotifyContextChange(
        GetController(),
        vcl::EnumContext::Context::Default);
}
 
void ScTabViewShell::Deactivate(bool bMDI)
{
    HideTip();
 
    ScDocument* pDoc=GetViewData().GetDocument();
 
    ScChangeTrack* pChanges=pDoc->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
 
        if ( pHdl )
            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.Width() > 0 && aObjSize.Height() > 0 )
        {
            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.Width() > 0 && aObjSize.Height() > 0 )
        {
            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* pDoc = rViewData.GetDocument();
    ScSplitPos ePos = rViewData.GetActivePart();
    SCCOL nCol = rViewData.GetPosX(WhichH(ePos));
    SCROW nRow = rViewData.GetPosY(WhichV(ePos));
    SCTAB nTab = rViewData.GetTabNo();
    bool bNegativePage = pDoc->IsNegativePage( nTab );
 
    tools::Rectangle aLogicRect = pDoc->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( const OUString& 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 = GetSdrView();
    if ( pDrView && !pDrView->AreObjectsMarked() && !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 )
    {
        if ( bActive )
        {
            bActiveDrawTextSh = bActiveDrawSh = false;
            bActiveDrawFormSh=false;
            bActiveGraphicSh=false;
            bActiveMediaSh=false;
            bActiveOleObjectSh=false;
            bActiveChartSh=false;
            SetCurSubShell(OST_Pivot);
        }
        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);
    }
    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 )
    {
        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 );
                }
                sal_uInt32 nCheckStatus = 0;
                if (svx::checkForSelectedFontWork(
                            GetScDrawView(), nCheckStatus )) {
                    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;
            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())
            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 *>( 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;
}
 
VclPtr<SfxTabPage> ScTabViewShell::CreatePrintOptionsPage(TabPageParent pParent, const SfxItemSet &rOptions )
{
    ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create();
    ::CreateTabPage ScTpPrintOptionsCreate = pFact->GetTabPageCreatorFunc(RID_SC_TP_PRINT);
    if ( ScTpPrintOptionsCreate )
        return ScTpPrintOptionsCreate(pParent, &rOptions);
    return VclPtr<SfxTabPage>();
}
 
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* pViewFrame = rShell.GetViewFrame();
    if (pViewFrame)
    {
        SfxFrame& rFrame = pViewFrame->GetFrame();
        uno::Reference<frame::XController> xController = rFrame.GetController();
        if (xController.is())
            pRet = ScTabViewObj::getImplementation( xController );
    }
    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* pViewFrm = 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.
 
        pViewFrm->GetFrame().Appear();
    }
 
    sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
 
    SC_MOD()->SetRefDialog( nId, true, pViewFrm );
 
    ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(pViewFrm->GetChildWindow( nId ));
    if (pWnd)
    {
        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 );
        vcl::Window* pWin = pWnd->GetWindow();
        pWin->SetText( rTitle );
        pWnd->StartRefInput();
    }
}
 
void ScTabViewShell::StopSimpleRefDialog()
{
    SfxViewFrame* pViewFrm = GetViewFrame();
    sal_uInt16 nId = ScSimpleRefDlgWrapper::GetChildWindowId();
 
    ScSimpleRefDlgWrapper* pWnd = static_cast<ScSimpleRefDlgWrapper*>(pViewFrm->GetChildWindow( nId ));
    if (pWnd)
    {
        vcl::Window* pWin = pWnd->GetWindow();
        if (pWin && pWin->IsSystemWindow())
            static_cast<SystemWindow*>(pWin)->Close();     // calls abort handler
    }
}
 
bool ScTabViewShell::TabKeyInput(const KeyEvent& rKEvt)
{
    ScModule* pScMod = SC_MOD();
 
    SfxViewFrame* pThisFrame = GetViewFrame();
    if ( pThisFrame->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* pDoc = GetViewData().GetDocument();
    if ( pDoc )
        pDoc->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;
            }
 
        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;
        }
        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(rDoc.GetDocOptions().IsAutoSpell());
 
    SetName("View"); // for SBX
    Color aColBlack( COL_BLACK );
    SetPool( &SC_MOD()->GetPool() );
    SetWindow( GetActiveWin() );
 
    pCurFrameLine.reset( new ::editeng::SvxBorderLine(&aColBlack, 20, SvxBorderLineStyle::SOLID) );
    pPivotSource.reset( new ScArea );
    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 );
                }
            }
 
            bool bReImport = false;                             // update imported data
            ScDBCollection* pDBColl = rDoc.GetDBCollection();
            if ( pDBColl )
            {
                const ScDBCollection::NamedDBs& rDBs = pDBColl->getNamedDBs();
                ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin(), itrEnd = rDBs.end();
                for (; itr != itrEnd; ++itr)
                {
                    if ((*itr)->IsStripData() && (*itr)->HasImportParam()
                        && !(*itr)->HasImportSelection())
                    {
                        bReImport = true;
                        break;
                    }
                }
            }
            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 );
}
 
ScTabViewShell::ScTabViewShell( SfxViewFrame* pViewFrame,
                                SfxViewShell* pOldSh ) :
    SfxViewShell( pViewFrame, SfxViewShellFlags::HAS_PRINTOPTIONS ),
    ScDBFunc( &pViewFrame->GetWindow(), static_cast<ScDocShell&>(*pViewFrame->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),
    nCurRefDlgId(0),
    mbInSwitch(false)
{
    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.get())
    {
        //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);
 
    // 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())
    {
        SfxViewShell* pViewShell = SfxViewShell::GetFirst();
        // have we already one view ?
        if (pViewShell)
        {
            // this view is not yet visible at this stage, so we look for not visible views, too
            SfxViewShell* pViewShell2 = SfxViewShell::GetNext(*pViewShell, /*only visible shells*/ false);
            // 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);
                ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
                if (pInputHdl && pInputHdl->IsFormulaMode())
                {
                    pInputHdl->SetMode(SC_INPUT_NONE);
                }
            }
        }
    }
}
 
ScTabViewShell::~ScTabViewShell()
{
    bInDispose = true;
 
    // Notify other LOK views that we are going away.
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false");
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", "");
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY");
    SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_CELL_VIEW_CURSOR, "rectangle", "EMPTY");
 
    // all to NULL, in case the TabView-dtor tries to access them
    //! (should not really! ??!?!)
    if (mpInputHandler)
    {
        mpInputHandler->SetDocumentDisposing(true);
        // We end edit mode, before destroying the input handler and the edit engine
        // and before end listening (in order to call ScTabViewShell::KillEditView())
        mpInputHandler->EnterHandler();
    }
 
    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);
 
    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();
    pAuditingShell.reset();
    pCurFrameLine.reset();
    mpInputHandler.reset();
    pPivotSource.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.GetName( 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();
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

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