/* -*- 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 <DrawViewShell.hxx>
#include <com/sun/star/scanner/ScannerManager.hpp>
#include <cppuhelper/implbase.hxx>
#include <comphelper/processfactory.hxx>
#include <editeng/sizeitem.hxx>
#include <svx/svdlayer.hxx>
#include <svx/svdograf.hxx>
#include <sfx2/zoomitem.hxx>
#include <sfx2/lokhelper.hxx>
#include <svx/svdpagv.hxx>
#include <svl/ptitem.hxx>
#include <svl/stritem.hxx>
#include <sfx2/request.hxx>
#include <sfx2/dispatch.hxx>
#include <svx/zoomslideritem.hxx>
#include <svl/eitem.hxx>

#include <sdcommands.h>
#include <svx/f3dchild.hxx>
#include <svx/clipfmtitem.hxx>

#include <sfx2/viewfrm.hxx>
#include <svtools/cliplistener.hxx>
#include <svx/float3d.hxx>
#include <svx/extedit.hxx>
#include <svx/sidebar/SelectionAnalyzer.hxx>
#include <svx/sidebar/SelectionChangeHandler.hxx>
#include <helpids.h>

#include <view/viewoverlaymanager.hxx>
#include <app.hrc>
#include <strings.hrc>
#include <sdmod.hxx>
#include <sdpage.hxx>
#include <FrameView.hxx>
#include <drawdoc.hxx>
#include <sdresid.hxx>
#include <DrawDocShell.hxx>
#include <Window.hxx>
#include <fupoor.hxx>
#include <fusel.hxx>
#include <funavig.hxx>
#include <drawview.hxx>
#include <SdUnoDrawView.hxx>
#include <ViewShellBase.hxx>
#include <slideshow.hxx>
#include <annotationmanager.hxx>
#include <DrawController.hxx>
#include <unomodel.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/servicehelper.hxx>
#include <LayerTabBar.hxx>

#include <memory>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using vcl::EnumContext;

namespace sd {

bool DrawViewShell::mbPipette = false;

namespace {

class ScannerEventListener : public ::cppu::WeakImplHelper< lang::XEventListener >
{
private:

    DrawViewShell*      mpParent;

public:

                            explicit ScannerEventListener( DrawViewShell* pParent ) : mpParent( pParent )  {}

    // XEventListener
    virtual void SAL_CALL   disposing( const lang::EventObject& rEventObject ) override;

    void                    ParentDestroyed() { mpParent = nullptr; }
};

}

void SAL_CALL ScannerEventListener::disposing( const lang::EventObject& /*rEventObject*/ )
{
    if( mpParent )
        mpParent->ScannerEvent();
}

DrawViewShell::DrawViewShell( ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, PageKind ePageKind, FrameView* pFrameViewArgument )
    : ViewShell (pParentWindow, rViewShellBase)
    , maTabControl(VclPtr<sd::TabControl>::Create(this, pParentWindow))
    , mbIsLayerModeActive(false)
    , mbIsInSwitchPage(false)
    , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
          [this] () { return this->GetSidebarContextName(); },
          uno::Reference<frame::XController>(rViewShellBase.GetDrawController()),
          vcl::EnumContext::Context::Default))
    , mbMouseButtonDown(false)
    , mbMouseSelecting(false)
{
    if (pFrameViewArgument != nullptr)
        mpFrameView = pFrameViewArgument;
    else
        mpFrameView = new FrameView(GetDoc());
    Construct(GetDocSh(), ePageKind);

    mpSelectionChangeHandler->Connect();

    SetContextName(GetSidebarContextName());

    doShow();

    ConfigureAppBackgroundColor();
    SdModule* mod = SdModule::get();
    mod->GetColorConfig().AddListener(this);

    if (comphelper::LibreOfficeKit::isActive())
    {
        // get the full page size in pixels
        mpContentWindow->EnableMapMode();
        Size aSize(mpContentWindow->LogicToPixel(GetView()->GetSdrPageView()->GetPage()->GetSize()));
        // Disable map mode, so that it's possible to send mouse event
        // coordinates in logic units
        mpContentWindow->EnableMapMode(false);

        // arrange UI elements again with new view size
        GetParentWindow()->SetSizePixel(aSize);
        Resize();

        SdXImpressDocument* pModel = comphelper::getFromUnoTunnel<SdXImpressDocument>(rViewShellBase.GetCurrentDocument());
        SfxLokHelper::notifyViewRenderState(&rViewShellBase, pModel);
    }
}

DrawViewShell::~DrawViewShell()
{
    suppress_fun_call_w_exception(ImplDestroy());
}

void DrawViewShell::ImplDestroy()
{
    destroyXSlideShowInstance();

    SdModule::get()->GetColorConfig().RemoveListener(this);

    mpSelectionChangeHandler->Disconnect();

    mpAnnotationManager.reset();
    mpViewOverlayManager.reset();

    OSL_ASSERT (GetViewShell()!=nullptr);

    if( mxScannerListener.is() )
        static_cast< ScannerEventListener* >( mxScannerListener.get() )->ParentDestroyed();

    // Remove references to items within Svx3DWin
    // (maybe do a listening sometime in Svx3DWin)
    sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId();
    SfxChildWindow* pWindow = GetViewFrame() ? GetViewFrame()->GetChildWindow(nId) : nullptr;
    if(pWindow)
    {
        Svx3DWin* p3DWin = static_cast< Svx3DWin* > (pWindow->GetWindow());
        if(p3DWin)
            p3DWin->DocumentReload();
    }

    EndListening (*GetDoc());
    EndListening (*GetDocSh());

    if( SlideShow::IsRunning(*this) )
        StopSlideShow();

    DisposeFunctions();

    sal_uInt16 aPageCnt = GetDoc()->GetSdPageCount(mePageKind);

    for (sal_uInt16 i = 0; i < aPageCnt; i++)
    {
        SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind);

        if (pPage == mpActualPage)
        {
            GetDoc()->SetSelected(pPage, true);
        }
        else
        {
            GetDoc()->SetSelected(pPage, false);
        }
    }

    if ( mxClipEvtLstnr.is() )
    {
        mxClipEvtLstnr->RemoveListener( GetActiveWindow() );
        mxClipEvtLstnr->ClearCallbackLink();        // prevent callback if another thread is waiting
        mxClipEvtLstnr.clear();
    }

    mpDrawView.reset();
    // Set mpView to NULL so that the destructor of the ViewShell base class
    // does not access it.
    mpView = nullptr;

    mpFrameView->Disconnect();
    maTabControl.disposeAndClear();
}

/**
 * common part of both constructors
 */
void DrawViewShell::Construct(DrawDocShell* pDocSh, PageKind eInitialPageKind)
{
    mpActualPage = nullptr;
    mbReadOnly = GetDocSh()->IsReadOnly();
    mxClipEvtLstnr.clear();
    mbPastePossible = false;
    mbIsLayerModeActive = false;

    mpFrameView->Connect();

    OSL_ASSERT (GetViewShell()!=nullptr);

    SetPool( &GetDoc()->GetPool() );

    GetDoc()->CreateFirstPages();

    mpDrawView.reset( new DrawView(pDocSh, GetActiveWindow()->GetOutDev(), this) );
    mpView = mpDrawView.get();             // Pointer of base class ViewShell
    mpDrawView->SetSwapAsynchron(); // Asynchronous load of graphics

    // We do not read the page kind from the frame view anymore so we have
    // to set it in order to resync frame view and this view.
    mpFrameView->SetPageKind(eInitialPageKind);
    mePageKind = eInitialPageKind;
    meEditMode = EditMode::Page;
    DocumentType eDocType = GetDoc()->GetDocumentType(); // RTTI does not work here
    switch (mePageKind)
    {
        case PageKind::Standard:
            meShellType = ST_IMPRESS;
            break;

        case PageKind::Notes:
            meShellType = ST_NOTES;
            break;

        case PageKind::Handout:
            meShellType = ST_HANDOUT;
            break;
    }

    Size aPageSize( GetDoc()->GetSdPage(0, mePageKind)->GetSize() );
    Point aPageOrg( aPageSize.Width(), aPageSize.Height() / 2);
    Size aSize(aPageSize.Width() * 3, aPageSize.Height() * 2);
    InitWindows(aPageOrg, aSize, Point(-1, -1));

    Point aVisAreaPos;

    if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        aVisAreaPos = pDocSh->GetVisArea(ASPECT_CONTENT).TopLeft();
    }

    mpDrawView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize));

    // objects can not grow bigger than ViewSize
    GetDoc()->SetMaxObjSize(aSize);

    // Split-Handler for TabControls
    maTabControl->SetSplitHdl( LINK( this, DrawViewShell, TabSplitHdl ) );

    /* In order to set the correct EditMode of the FrameView, we select another
       one (small trick).  */
    if (mpFrameView->GetViewShEditMode(/*mePageKind*/) == EditMode::Page)
    {
        meEditMode = EditMode::MasterPage;
    }
    else
    {
        meEditMode = EditMode::Page;
    }

    // Use configuration of FrameView
    ReadFrameViewData(mpFrameView);

    if( eDocType == DocumentType::Draw )
    {
        GetActiveWindow()->SetHelpId( HID_SDGRAPHICVIEWSHELL );
    }
    else
    {
        if (mePageKind == PageKind::Notes)
        {
            GetActiveWindow()->SetHelpId( CMD_SID_NOTES_MODE );

            // AutoLayouts have to be created
            GetDoc()->StopWorkStartupDelay();
        }
        else if (mePageKind == PageKind::Handout)
        {
            GetActiveWindow()->SetHelpId( CMD_SID_HANDOUT_MASTER_MODE );

            // AutoLayouts have to be created
            GetDoc()->StopWorkStartupDelay();
        }
        else
        {
            GetActiveWindow()->SetHelpId( HID_SDDRAWVIEWSHELL );
        }
    }

    // start selection function
    SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool());
    FuPermanent(aReq);
    mpDrawView->SetFrameDragSingles();

    if (pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)
    {
        mbZoomOnPage = false;
    }
    else
    {
        mbZoomOnPage = true;
    }

    mbIsRulerDrag = false;

    SetName (u"DrawViewShell"_ustr);

    mnLockCount = 0;

    const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );

    try
    {
        mxScannerManager = scanner::ScannerManager::create( xContext );

        mxScannerListener = new ScannerEventListener( this );
    }
    catch (DeploymentException const &)
    {
        SAL_INFO("sd", "Scanner manager not available");
    }
    catch (Exception const &)
    {
        // Eat the exception and log it
        // We can still continue if scanner manager is not available.
        DBG_UNHANDLED_EXCEPTION("sd");
    }

    mpAnnotationManager.reset( new AnnotationManager( GetViewShellBase() ) );
    mpViewOverlayManager.reset( new ViewOverlayManager( GetViewShellBase() ) );
}

void DrawViewShell::Init (bool bIsMainViewShell)
{
    ViewShell::Init(bIsMainViewShell);

    if (!IsListening(*GetDocSh()))
        StartListening (*GetDocSh());
}

void DrawViewShell::Shutdown()
{
    ViewShell::Shutdown();

    if(SlideShow::IsRunning( GetViewShellBase() )
        && !SlideShow::IsInteractiveSlideshow( GetViewShellBase() )) // IASS
    {
        // Turn off effects.
        GetDrawView()->SetAnimationMode(SdrAnimationMode::Disable);
    }
}

css::uno::Reference<css::drawing::XDrawSubController> DrawViewShell::CreateSubController()
{
    css::uno::Reference<css::drawing::XDrawSubController> xSubController;

    if (IsMainViewShell())
    {
        // Create uno sub controller for the main view shell.
        xSubController.set( new SdUnoDrawView( *this, *GetView()));
    }

    return xSubController;
}

bool DrawViewShell::RelocateToParentWindow (vcl::Window* pParentWindow)
{
    // DrawViewShells can not be relocated to a new parent window at the
    // moment, so return <FALSE/> except when the given parent window is the
    // parent window that is already in use.
    return pParentWindow==GetParentWindow();
}

/**
 * check if we have to draw a polyline
 */

/*
    Polylines are represented by macros as a sequence of:
        MoveTo (x, y)
        LineTo (x, y)   [or BezierTo (x, y)]
        LineTo (x, y)
            :
    There is no end command for polylines. Therefore, we have to test all
    commands in the requests for LineTo (BezierTo) and we have to gather
    the point-parameter. The first not-LineTo leads to the creation of the
    polyline from the gathered points.
*/

void DrawViewShell::CheckLineTo(SfxRequest& rReq)
{
#ifdef DBG_UTIL
    if(rReq.IsAPI())
    {
        if(SID_LINETO == rReq.GetSlot() || SID_BEZIERTO == rReq.GetSlot() || SID_MOVETO == rReq.GetSlot() )
        {
            OSL_FAIL("DrawViewShell::CheckLineTo: slots SID_LINETO, SID_BEZIERTO, SID_MOVETO no longer supported.");
        }
    }
#endif

    rReq.Ignore ();
}

/**
 * Change page parameter if SID_PAGESIZE or SID_PAGEMARGIN
 */
void DrawViewShell::SetupPage (Size const &rSize,
                                 ::tools::Long nLeft,
                                 ::tools::Long nRight,
                                 ::tools::Long nUpper,
                                 ::tools::Long nLower,
                                 bool bSize,
                                 bool bMargin,
                                 bool bScaleAll)
{
    sal_uInt16 nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind);
    sal_uInt16 i;

    for (i = 0; i < nPageCnt; i++)
    {
        // first, handle all master pages
        SdPage *pPage = GetDoc()->GetMasterSdPage(i, mePageKind);

        if( pPage )
        {
            if( bSize )
            {
                ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower);
                pPage->ScaleObjects(rSize, aBorderRect, bScaleAll);
                pPage->SetSize(rSize);

            }
            if( bMargin )
            {
                pPage->SetLeftBorder(nLeft);
                pPage->SetRightBorder(nRight);
                pPage->SetUpperBorder(nUpper);
                pPage->SetLowerBorder(nLower);
            }

            if ( mePageKind == PageKind::Standard )
            {
                GetDoc()->GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout();
            }

            pPage->CreateTitleAndLayout();
        }
    }

    nPageCnt = GetDoc()->GetSdPageCount(mePageKind);

    for (i = 0; i < nPageCnt; i++)
    {
        // then, handle all pages
        SdPage *pPage = GetDoc()->GetSdPage(i, mePageKind);

        if( pPage )
        {
            if( bSize )
            {
                ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower);
                pPage->ScaleObjects(rSize, aBorderRect, bScaleAll);
                pPage->SetSize(rSize);
            }
            if( bMargin )
            {
                pPage->SetLeftBorder(nLeft);
                pPage->SetRightBorder(nRight);
                pPage->SetUpperBorder(nUpper);
                pPage->SetLowerBorder(nLower);
            }

            if ( mePageKind == PageKind::Standard )
            {
                SdPage* pNotesPage = GetDoc()->GetSdPage(i, PageKind::Notes);
                pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() );
            }

            pPage->SetAutoLayout( pPage->GetAutoLayout() );
        }
    }

    if ( mePageKind == PageKind::Standard )
    {
        SdPage* pHandoutPage = GetDoc()->GetSdPage(0, PageKind::Handout);
        pHandoutPage->CreateTitleAndLayout(true);
    }

    ::tools::Long nWidth = mpActualPage->GetSize().Width();
    ::tools::Long nHeight = mpActualPage->GetSize().Height();

    Point aPageOrg(nWidth, nHeight / 2);
    Size aSize( nWidth * 3, nHeight * 2);

    InitWindows(aPageOrg, aSize, Point(-1, -1), true);

    Point aVisAreaPos;

    if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
    {
        aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft();
    }

    GetView()->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize));

    UpdateScrollBars();

    Point aNewOrigin(mpActualPage->GetLeftBorder(), mpActualPage->GetUpperBorder());
    GetView()->GetSdrPageView()->SetPageOrigin(aNewOrigin);

    GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET);

    // zoom onto (new) page size
    GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE,
                        SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
}

void DrawViewShell::GetStatusBarState(SfxItemSet& rSet)
{
    /* Zoom-Item
       Here we should propagate the corresponding value (Optimal ?, page width
       or page) with the help of the ZoomItems !!!   */
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOM ) )
    {
        if (GetDocSh()->IsUIActive()
            || (SlideShow::IsRunning(GetViewShellBase()) && !SlideShow::IsInteractiveSlideshow(GetViewShellBase())) // IASS
            || !GetActiveWindow())
        {
            rSet.DisableItem( SID_ATTR_ZOOM );
        }
        else
        {
            std::unique_ptr<SvxZoomItem> pZoomItem;
            sal_uInt16 nZoom = static_cast<sal_uInt16>(GetActiveWindow()->GetZoom());

            if( mbZoomOnPage )
                pZoomItem.reset(new SvxZoomItem( SvxZoomType::WHOLEPAGE, nZoom ));
            else
                pZoomItem.reset(new SvxZoomItem( SvxZoomType::PERCENT, nZoom ));

            // constrain area
            SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL;
            SdrPageView* pPageView = mpDrawView->GetSdrPageView();

            if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 )
            {
                nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL;
            }

            pZoomItem->SetValueSet( nZoomValues );
            rSet.Put( std::move(pZoomItem) );
        }
    }
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOMSLIDER ) )
    {
        rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetDoc() ) );
        if (GetDocSh()->IsUIActive()
            || (xSlideshow.is() && xSlideshow->isRunning() && !xSlideshow->IsInteractiveSlideshow()) // IASS
            || !GetActiveWindow() )
        {
            rSet.DisableItem( SID_ATTR_ZOOMSLIDER );
        }
        else
        {
            sd::Window * pActiveWindow = GetActiveWindow();
            SvxZoomSliderItem aZoomItem( static_cast<sal_uInt16>(pActiveWindow->GetZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMinZoom()), static_cast<sal_uInt16>(pActiveWindow->GetMaxZoom()) ) ;

            SdrPageView* pPageView = mpDrawView->GetSdrPageView();
            if( pPageView )
            {
                Point aPagePos(0, 0);
                Size aPageSize = pPageView->GetPage()->GetSize();

                aPagePos.AdjustX(aPageSize.Width()  / 2 );
                aPageSize.setWidth( static_cast<::tools::Long>(aPageSize.Width() * 1.03) );

                aPagePos.AdjustY(aPageSize.Height() / 2 );
                aPageSize.setHeight( static_cast<::tools::Long>(aPageSize.Height() * 1.03) );
                aPagePos.AdjustY( -(aPageSize.Height() / 2) );

                aPagePos.AdjustX( -(aPageSize.Width()  / 2) );

                ::tools::Rectangle aFullPageZoomRect( aPagePos, aPageSize );
                aZoomItem.AddSnappingPoint( pActiveWindow->GetZoomForRect( aFullPageZoomRect ) );
            }
            aZoomItem.AddSnappingPoint(100);
            rSet.Put( aZoomItem );
        }
    }

    SdrPageView* pPageView = mpDrawView->GetSdrPageView();
    if (pPageView)
    {
        Point aPos = GetActiveWindow()->PixelToLogic(maMousePos);
        pPageView->LogicToPagePos(aPos);
        Fraction aUIScale(GetDoc()->GetUIScale());
        aPos.setX( ::tools::Long(aPos.X() / aUIScale) );
        aPos.setY( ::tools::Long(aPos.Y() / aUIScale) );

        // position- and size items
        if ( mpDrawView->IsAction() )
        {
            ::tools::Rectangle aRect;
            mpDrawView->TakeActionRect( aRect );

            if ( aRect.IsEmpty() )
                rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) );
            else
            {
                pPageView->LogicToPagePos(aRect);
                aPos = aRect.TopLeft();
                aPos.setX( ::tools::Long(aPos.X() / aUIScale) );
                aPos.setY( ::tools::Long(aPos.Y() / aUIScale) );
                rSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos) );
                Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() );
                aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) );
                aSize.setWidth( ::tools::Long(aSize.Width()  / aUIScale) );
                rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) );
            }
        }
        else
        {
            const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList();
            if ( rMarkList.GetMarkCount() != 0 )
            {
                ::tools::Rectangle aRect = mpDrawView->GetAllMarkedRect();
                pPageView->LogicToPagePos(aRect);

                // Show the position of the selected shape(s)
                Point aShapePosition (aRect.TopLeft());
                aShapePosition.setX( ::tools::Long(aShapePosition.X() / aUIScale) );
                aShapePosition.setY( ::tools::Long(aShapePosition.Y() / aUIScale) );
                rSet.Put (SfxPointItem(SID_ATTR_POSITION, aShapePosition));

                Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() );
                aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) );
                aSize.setWidth( ::tools::Long(aSize.Width()  / aUIScale) );
                rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) );
            }
            else
            {
                rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) );
                rSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) );
            }
        }
    }

    // Display of current page and layer.
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_PAGE ) )
    {
        sal_Int32 nPageCount = sal_Int32(GetDoc()->GetSdPageCount(mePageKind));
        sal_Int32 nActivePageCount = sal_Int32(GetDoc()->GetActiveSdPageCount());
        // Always show the slide/page number.
        OUString aOUString;
        if (GetDoc()->GetDocumentType() == DocumentType::Draw)
            aOUString = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT_DRAW) : SdResId(STR_SD_PAGE_COUNT_CUSTOM_DRAW);
        else
            aOUString = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT) : SdResId(STR_SD_PAGE_COUNT_CUSTOM);

        aOUString = aOUString.replaceFirst("%1", OUString::number(maTabControl->GetCurPagePos() + 1));
        aOUString = aOUString.replaceFirst("%2", OUString::number(nPageCount));
        if(nPageCount != nActivePageCount)
            aOUString = aOUString.replaceFirst("%3", OUString::number(nActivePageCount));

        // If in layer mode additionally show the layer that contains all
        // selected shapes of the page.  If the shapes are distributed on
        // more than one layer, no layer name is shown.
        if (IsLayerModeActive())
        {
            SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin();
            SdrLayerID nLayer(0), nOldLayer(0);
            SdrObject* pObj = nullptr;
            const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList();
            const size_t nMarkCount = rMarkList.GetMarkCount();
            bool bOneLayer = true;

            // Use the first ten selected shapes as a (hopefully
            // representative) sample of all shapes of the current page.
            // Detect whether they belong to the same layer.
            for( size_t j = 0; j < nMarkCount && bOneLayer && j < 10; ++j )
            {
                pObj = rMarkList.GetMark( j )->GetMarkedSdrObj();
                if( pObj )
                {
                    nLayer = pObj->GetLayer();

                    if( j != 0 && nLayer != nOldLayer )
                        bOneLayer = false;

                    nOldLayer = nLayer;
                }
            }

            // Append the layer name to the current page number.
            if( bOneLayer && nMarkCount )
            {
                SdrLayer* pLayer = rLayerAdmin.GetLayerPerID( nLayer );
                if( pLayer )
                {
                    aOUString += " (" + LayerTabBar::convertToLocalizedName(pLayer->GetName()) + ")";
                }
            }
        }

        rSet.Put (SfxStringItem (SID_STATUS_PAGE, aOUString));
    }
    // Layout
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_LAYOUT ) )
    {
        OUString aString = mpActualPage->GetLayoutName();
        sal_Int32 nPos = aString.indexOf(SD_LT_SEPARATOR);
        if (nPos != -1)
            aString = aString.copy(0, nPos);
        rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aString ) );
    }
    // Scale
    if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SCALE ) )
    {
        const Fraction& aUIScale = GetDoc()->GetUIScale();
        OUString aString = OUString::number(aUIScale.GetNumerator()) +
            ":" + OUString::number(aUIScale.GetDenominator());
        rSet.Put( SfxStringItem( SID_SCALE, aString ) );
    }
}

void DrawViewShell::Notify (SfxBroadcaster&, const SfxHint& rHint)
{
    if (rHint.GetId()!=SfxHintId::ModeChanged)
        return;

    // Change to selection when turning on read-only mode.
    if(GetDocSh()->IsReadOnly() && dynamic_cast< FuSelection* >( GetCurrentFunction().get() ) )
    {
        SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool());
        FuPermanent(aReq);
    }

    // Turn on design mode when document is not read-only.
    if (GetDocSh()->IsReadOnly() != mbReadOnly )
    {
        mbReadOnly = GetDocSh()->IsReadOnly();

        SfxBoolItem aItem( SID_FM_DESIGN_MODE, !mbReadOnly );
        GetViewFrame()->GetDispatcher()->ExecuteList(SID_FM_DESIGN_MODE,
            SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
    }

}

void DrawViewShell::ExecuteAnnotation (SfxRequest const & rRequest)
{
    if (mpAnnotationManager)
        mpAnnotationManager->ExecuteAnnotation( rRequest );
}

void DrawViewShell::GetAnnotationState (SfxItemSet& rItemSet )
{
    if (mpAnnotationManager)
        mpAnnotationManager->GetAnnotationState( rItemSet );
}

OUString const & DrawViewShell::GetSidebarContextName() const
{
    svx::sidebar::SelectionAnalyzer::ViewType eViewType (svx::sidebar::SelectionAnalyzer::ViewType::Standard);
    switch (mePageKind)
    {
        case PageKind::Handout:
            eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Handout;
            break;
        case PageKind::Notes:
            eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Notes;
            break;
        case PageKind::Standard:
            if (meEditMode == EditMode::MasterPage)
                eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Master;
            else
                eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Standard;
            break;
    }
    const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList();
    return EnumContext::GetContextName(
        svx::sidebar::SelectionAnalyzer::GetContextForSelection_SD(
            rMarkList,
            eViewType));
}

void DrawViewShell::ExecGoToNextPage (SfxRequest& rReq)
{
    SetCurrentFunction( FuNavigation::Create( *this, GetActiveWindow(), mpDrawView.get(), *GetDoc(), rReq) );
    Cancel();
}

void DrawViewShell::GetStateGoToNextPage (SfxItemSet& rSet)
{
    SdPage* pPage = GetActualPage();
    sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2;
    sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind());
    if (nSdPage + 1 >= totalPages)
        rSet.DisableItem( SID_GO_TO_NEXT_PAGE );
}

void DrawViewShell::ExecGoToPreviousPage (SfxRequest& rReq)
{
    SetCurrentFunction( FuNavigation::Create( *this, GetActiveWindow(), mpDrawView.get(), *GetDoc(), rReq) );
    Cancel();
}

void DrawViewShell::GetStateGoToPreviousPage (SfxItemSet& rSet)
{
    SdPage* pPage = GetActualPage();
    sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2;
    if (nSdPage == 0)
        rSet.DisableItem( SID_GO_TO_PREVIOUS_PAGE );
}


void DrawViewShell::ExecGoToFirstPage (SfxRequest& rReq)
{
    SetCurrentFunction( FuNavigation::Create( *this, GetActiveWindow(), mpDrawView.get(), *GetDoc(), rReq) );
    Cancel();
}

void DrawViewShell::GetStateGoToFirstPage (SfxItemSet& rSet)
{
    SdPage* pPage = GetActualPage();
    sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2;
    if (nSdPage == 0)
        rSet.DisableItem( SID_GO_TO_FIRST_PAGE );
}

void DrawViewShell::ExecGoToLastPage (SfxRequest& rReq)
{
    SetCurrentFunction( FuNavigation::Create( *this, GetActiveWindow(), mpDrawView.get(), *GetDoc(), rReq) );
    Cancel();
}

void DrawViewShell::GetStateGoToLastPage (SfxItemSet& rSet)
{
    SdPage* pPage = GetActualPage();
    sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2;
    sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind());
    if (nSdPage + 1 >= totalPages)
        rSet.DisableItem( SID_GO_TO_LAST_PAGE );
}

void DrawViewShell::ExecGoToPage (SfxRequest& rReq)
{
    SetCurrentFunction( FuNavigation::Create( *this, GetActiveWindow(), mpDrawView.get(), *GetDoc(), rReq) );
    Cancel();
}

void DrawViewShell::GetStateGoToPage (SfxItemSet& rSet)
{
    if (meEditMode == EditMode::MasterPage)
        rSet.DisableItem( SID_GO_TO_PAGE );
}

} // end of namespace sd

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