/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the Collabora Office 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 <memory>
#include <sal/config.h>

#include <osl/diagnose.h>
#include <osl/file.hxx>
#include <tools/urlobj.hxx>
#include <sfx2/fcontnr.hxx>
#include <svl/stritem.hxx>
#include <sfx2/docfile.hxx>
#include <svl/intitem.hxx>
#include <sfx2/dispatch.hxx>

#include <sfx2/viewfrm.hxx>

#include <pres.hxx>
#include <navigatr.hxx>
#include <pgjump.hxx>
#include <app.hrc>

#include <bitmaps.hlst>
#include <drawdoc.hxx>
#include <DrawDocShell.hxx>
#include <ViewShell.hxx>
#include <ViewShellBase.hxx>
#include <slideshow.hxx>
#include <FrameView.hxx>
#include <Window.hxx>

#include <DrawViewShell.hxx>
#include <utility>

#include <vcl/jsdialog/executor.hxx>

#include <vcl/commandevent.hxx>
#include <comphelper/kit.hxx>

#include <sdpage.hxx>

/**
 * SdNavigatorWin - FloatingWindow
 */
SdNavigatorWin::SdNavigatorWin(weld::Widget* pParent, SfxBindings* pInBindings, SfxNavigator* pNavigatorDlg)
    : PanelLayout(pParent, u"NavigatorPanel"_ustr, u"modules/simpress/ui/navigatorpanel.ui"_ustr)
    , mxToolbox(m_xBuilder->weld_toolbar(u"toolbox"_ustr))
    , mxTlbObjects(new SdPageObjsTLV(m_xBuilder->weld_tree_view(u"tree"_ustr)))
    , mxLbDocs(m_xBuilder->weld_combo_box(u"documents"_ustr))
    , mxDragModeMenu(m_xBuilder->weld_menu(u"dragmodemenu"_ustr))
    , mxShapeMenu(m_xBuilder->weld_menu(u"shapemenu"_ustr))
    , mxNavigatorDlg(pNavigatorDlg)
    , mbDocImported ( false )
      // On changes of the DragType: adjust SelectionMode of TLB!
    , meDragType ( NAVIGATOR_DRAGTYPE_EMBEDDED )
    , mpBindings ( pInBindings )
{
    if (comphelper::COKit::isActive())
    {
        sal_uInt64 nShellId = reinterpret_cast<sal_uInt64>(SfxViewShell::Current());
        jsdialog::SendNavigatorForView(nShellId);
    }

    mxTlbObjects->SetViewFrame( mpBindings->GetDispatcher()->GetFrame() );

    mxTlbObjects->connect_row_activated(LINK(this, SdNavigatorWin, ClickObjectHdl));
    mxTlbObjects->set_selection_mode(SelectionMode::Multiple);
    mxTlbObjects->connect_mouse_release(LINK(this, SdNavigatorWin, MouseReleaseHdl));
    mxTlbObjects->connect_popup_menu(LINK(this, SdNavigatorWin, CommandHdl));

    mxToolbox->connect_clicked(LINK(this, SdNavigatorWin, SelectToolboxHdl));
    mxToolbox->connect_menu_toggled(LINK(this, SdNavigatorWin, DropdownClickToolBoxHdl));

    mxToolbox->set_item_menu(u"dragmode"_ustr, mxDragModeMenu.get());
    mxDragModeMenu->connect_activate(LINK(this, SdNavigatorWin, MenuSelectHdl));

    // Shape filter drop down menu.
    mxToolbox->set_item_menu(u"shapes"_ustr, mxShapeMenu.get());
    mxShapeMenu->connect_activate(LINK(this, SdNavigatorWin, ShapeFilterCallback));

    mxTlbObjects->SetSdNavigator(this);

    // DragTypeListBox
    mxLbDocs->set_size_request(42, -1); // set a nominal width so it takes width of surroundings

    SetDragImage();

    if(comphelper::COKit::isActive())
    {
        mxToolbox->hide();
        mxLbDocs->hide();
    }
}

void SdNavigatorWin::FirstFocus()
{
    // set focus to listbox, otherwise it is in the toolbox which is only useful
    // for keyboard navigation
    mxTlbObjects->grab_focus();
}

weld::Window* SdNavigatorWin::GetFrameWeld() const
{
    if (mxNavigatorDlg)
        return mxNavigatorDlg->GetFrameWeld();
    return PanelLayout::GetFrameWeld();
}

void SdNavigatorWin::SetUpdateRequestFunctor(const UpdateRequestFunctor& rUpdateRequest)
{
    mpNavigatorCtrlItem.reset( new SdNavigatorControllerItem(SID_NAVIGATOR_STATE, this, mpBindings, rUpdateRequest) );
    mpPageNameCtrlItem.reset( new SdPageNameControllerItem(SID_NAVIGATOR_PAGENAME, this, mpBindings) );

    // InitTlb; is initiated over Slot
    if (rUpdateRequest)
        rUpdateRequest();
}

SdNavigatorWin::~SdNavigatorWin()
{
    mpNavigatorCtrlItem.reset();
    mpPageNameCtrlItem.reset();
    mxDragModeMenu.reset();
    mxShapeMenu.reset();
    mxToolbox.reset();
    mxTlbObjects.reset();
    mxLbDocs.reset();
}

static void lcl_select_marked_objects(sd::ViewShell* pViewShell, SdPageObjsTLV* pTlbObjects)
{
    if (const SdrView* pView = pViewShell->GetDrawView())
    {
        auto vMarkedObjects = pView->GetMarkedObjects();
        if (vMarkedObjects.size())
        {
            pTlbObjects->unselect_all();
            for (auto rMarkedObject: vMarkedObjects)
                pTlbObjects->SelectEntry(rMarkedObject);
        }
        else
        {
            if (SdPage* pPage = pViewShell->GetActualPage())
                pTlbObjects->SelectEntry(pPage->GetName());
        }
    }
}

void SdNavigatorWin::InitTreeLB( const SdDrawDocument* pDoc )
{
    SdDrawDocument* pNonConstDoc = const_cast<SdDrawDocument*>(pDoc); // const as const can...
    ::sd::DrawDocShell* pDocShell = pNonConstDoc->GetDocSh();
    OUString aDocShName( pDocShell->GetName() );
    ::sd::ViewShell* pViewShell = pDocShell->GetViewShell();

    // tdf#160190
    if (!pViewShell)
        return;

    // tdf#139944 disable navigator in master mode
    if (const sd::DrawViewShell* pDrawViewShell = static_cast<::sd::DrawViewShell*>(pViewShell))
    {
        if (pDrawViewShell->GetEditMode() == EditMode::MasterPage)
        {
            m_xContainer->set_sensitive(false);
            mxTlbObjects->clear();
            RefreshDocumentLB();
            return;
        }
        else
            m_xContainer->set_sensitive(true);
    }

    // Restore the 'ShowAllShapes' flag from the last time (in this session)
    // that the navigator was shown.
    ::sd::FrameView* pFrameView = pViewShell->GetFrameView();
    if (pFrameView != nullptr)
        mxTlbObjects->SetShowAllShapes(pFrameView->IsNavigatorShowingAllShapes(), false);

    // Disable the shape filter drop down menu when there is a running slide
    // show.
    mxToolbox->set_item_sensitive(u"shapes"_ustr, true);

    if( !mxTlbObjects->IsEqualToDoc( pDoc ) )
    {
        OUString aDocName = pDocShell->GetMedium()->GetName();
        mxTlbObjects->Fill( pDoc, false, aDocName ); // only normal pages

        RefreshDocumentLB();
        mxLbDocs->set_active_text(aDocShName);
    }
    else
    {
        mxLbDocs->set_active(-1);
        mxLbDocs->set_active_text(aDocShName);

// commented in order to fix 30246
//        if( mxLbDocs->get_active() == -1 )
        {
            RefreshDocumentLB();
            mxLbDocs->set_active_text(aDocShName);
        }
    }

    lcl_select_marked_objects(pViewShell, mxTlbObjects.get());
}

/**
 * DragType is set on dependence if a Drag is even possible. For example,
 * under certain circumstances, it is not allowed to drag graphics (#31038#).
 */
NavigatorDragType SdNavigatorWin::GetNavigatorDragType()
{
    NavigatorDragType   eDT = meDragType;
    NavDocInfo*         pInfo = GetDocInfo();

    if( ( eDT == NAVIGATOR_DRAGTYPE_LINK ) && ( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() ) )
        eDT = NAVIGATOR_DRAGTYPE_NONE;

    return eDT;
}

SdPageObjsTLV& SdNavigatorWin::GetObjects()
{
    return *mxTlbObjects;
}

IMPL_STATIC_LINK_NOARG(SdNavigatorWin, MouseReleaseHdl, const MouseEvent&, bool)
{
    return true;
}

IMPL_LINK(SdNavigatorWin, CommandHdl, const CommandEvent&, rCEvt, bool)
{
    if (comphelper::COKit::isActive())
        return false;

    if (NavDocInfo* pInfo = GetDocInfo(); !pInfo || !pInfo->IsActive())
        return false;
    if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
        return false;
    weld::TreeView& rTreeView = GetObjects().get_treeview();
    std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&rTreeView,
                                            u"modules/sdraw/ui/navigatorcontextmenu.ui"_ustr));
    std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu(u"navmenu"_ustr);
    OUString sCommand = xPop->popup_at_rect(&rTreeView,
                                           tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)));
    if (!sCommand.isEmpty())
        ExecuteContextMenuAction(sCommand);
    return true;
}

void SdNavigatorWin::ExecuteContextMenuAction(std::u16string_view rSelectedPopupEntry)
{
    if (rSelectedPopupEntry == u"rename")
    {
        weld::TreeView& rTreeView = GetObjects().get_treeview();
        std::unique_ptr<weld::TreeIter> xIter(rTreeView.make_iterator());
        if (rTreeView.get_selected(xIter.get()))
            rTreeView.start_editing(*xIter);
    }
}

IMPL_LINK(SdNavigatorWin, SelectToolboxHdl, const OUString&, rCommand, void)
{
    PageJump ePage = PAGE_NONE;

    if (rCommand == "first")
        ePage = PAGE_FIRST;
    else if (rCommand == "previous")
        ePage = PAGE_PREVIOUS;
    else if (rCommand == "next")
        ePage = PAGE_NEXT;
    else if (rCommand == "last")
        ePage = PAGE_LAST;
    else if (rCommand == "dragmode")
        mxToolbox->set_menu_item_active(u"dragmode"_ustr, !mxToolbox->get_menu_item_active(u"dragmode"_ustr));
    else if (rCommand == "shapes")
        mxToolbox->set_menu_item_active(u"shapes"_ustr, !mxToolbox->get_menu_item_active(u"shapes"_ustr));

    if (ePage != PAGE_NONE)
    {
        SfxUInt16Item aItem( SID_NAVIGATOR_PAGE, static_cast<sal_uInt16>(ePage) );
        mpBindings->GetDispatcher()->ExecuteList(SID_NAVIGATOR_PAGE,
                SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });
    }
}

IMPL_LINK(SdNavigatorWin, DropdownClickToolBoxHdl, const OUString&, rCommand, void)
{
    if (!mxToolbox->get_menu_item_active(rCommand))
        return;

    if (rCommand == "dragmode")
    {
        NavDocInfo* pInfo = GetDocInfo();
        if( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() )
        {
            mxDragModeMenu->set_sensitive(OUString::number(NAVIGATOR_DRAGTYPE_LINK), false);
            mxDragModeMenu->set_sensitive(OUString::number(NAVIGATOR_DRAGTYPE_URL), false);
            meDragType = NAVIGATOR_DRAGTYPE_EMBEDDED;
        }

        mxDragModeMenu->set_active(OUString::number(meDragType), true);
    }
    else if (rCommand == "shapes")
    {
        bool bAll = mxTlbObjects->GetShowAllShapes();
        mxShapeMenu->set_active(u"named"_ustr, !bAll);
        mxShapeMenu->set_active(u"all"_ustr, bAll);
        bool bOrderFrontToBack = mxTlbObjects->GetOrderFrontToBack();
        mxShapeMenu->set_active(u"fronttoback"_ustr, bOrderFrontToBack);
        mxShapeMenu->set_active(u"backtofront"_ustr, !bOrderFrontToBack);
    }
}

IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool)
{
    if( !mbDocImported || mxLbDocs->get_active() != 0 )
    {
        NavDocInfo* pInfo = GetDocInfo();

        // if it is the active window, we jump to the page
        if( pInfo && pInfo->IsActive() )
        {
            OUString aStr(mxTlbObjects->get_cursor_text());

            if( !aStr.isEmpty() )
            {
                sd::DrawDocShell* pDocShell = pInfo->mpDocShell;
                if (!pDocShell)
                    return false;
                sd::ViewShell* pViewShell = pDocShell->GetViewShell();
                if (!pViewShell)
                    return false;
                SdrView* pDrawView = pViewShell->GetDrawView();
                if (!pDrawView)
                    return false;

                // Save the selected tree entries re-mark the objects in the view after navigation.
                auto vSelectedEntryIds = mxTlbObjects->GetSelectedEntryIds();

                // Page entries in the tree have id value 1. Object entries have id value of
                // the address of the pointer to the object.
                const auto aCursorEntryId = mxTlbObjects->get_cursor_id();
                auto nCursorEntryId = aCursorEntryId.toInt64();
                SdrObject* pCursorEntryObject = weld::fromId<SdrObject*>(aCursorEntryId);

                bool bIsCursorEntrySelected(std::find(vSelectedEntryIds.begin(),
                                                      vSelectedEntryIds.end(),
                                                      aCursorEntryId) != vSelectedEntryIds.end());

                if (bIsCursorEntrySelected)
                {
                    // Set a temporary name, if need be, so the object can be navigated to.
                    bool bCursorEntryObjectHasEmptyName = false;
                    if (nCursorEntryId != 1 && pCursorEntryObject
                            && pCursorEntryObject->GetName().isEmpty())
                    {
                        bCursorEntryObjectHasEmptyName = true;
                        bool bUndo = pCursorEntryObject->getSdrModelFromSdrObject().IsUndoEnabled();
                        pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(false);
                        pCursorEntryObject->SetName(aStr, false);
                        pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
                    }

                    // All objects are unmarked when navigating to an object.
                    SfxStringItem aItem(SID_NAVIGATOR_OBJECT, aStr);
                    mpBindings->GetDispatcher()->ExecuteList(SID_NAVIGATOR_OBJECT,
                                            SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });

                    if (bCursorEntryObjectHasEmptyName)
                    {
                        bool bUndo = pCursorEntryObject->getSdrModelFromSdrObject().IsUndoEnabled();
                        pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(false);
                        pCursorEntryObject->SetName(OUString(), false);
                        pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
                    }

                    // re-mark the objects
                    if (bIsCursorEntrySelected)
                    {
                        // Mark the objects in the view that are selected in the Navigator tree.
                        for (auto& rEntryId: vSelectedEntryIds)
                        {
                            if (rEntryId != "1")
                            {
                                SdrObject* pEntryObject = weld::fromId<SdrObject*>(rEntryId);
                                if (pEntryObject)
                                    pDrawView->MarkObj(pEntryObject, pDrawView->GetSdrPageView());
                            }
                        }
                    }
                }
                else if (nCursorEntryId != 1 && pCursorEntryObject)
                {
                    // unmark
                    pDrawView->MarkObj(pCursorEntryObject, pDrawView->GetSdrPageView(), true);
                }

                // SID_NAVIGATOR_STATE invalidate is done in DrawViewShell::ExecNavigatorWin
                // and DrawDocShell::GotoBookmark. Update the bindings here to speed up Navigator
                // state update.
                mpBindings->Update();

                if (mxTlbObjects->IsNavigationGrabsFocus())
                {
                    // moved here from SetGetFocusHdl. Reset the
                    // focus only if something has been selected in the
                    // document.
                    SfxViewShell* pCurSh = SfxViewShell::Current();

                    if ( pCurSh )
                    {
                        vcl::Window* pShellWnd = pCurSh->GetWindow();
                        if ( pShellWnd )
                            pShellWnd->GrabFocus();
                    }

                    // We navigated to an object, but the current shell may be
                    // still the slide sorter. Explicitly try to grab the draw
                    // shell focus, so follow-up operations work with the object
                    // and not with the whole slide.
                    vcl::Window* pWindow = pViewShell->GetActiveWindow();
                    if (pWindow)
                        pWindow->GrabFocus();
                }
            }
        }
    }
    return false;
}

/**
 * Set DrageType and set image accordingly to it.
 */
IMPL_LINK(SdNavigatorWin, MenuSelectHdl, const OUString&, rIdent, void)
{
    sal_uInt32 nMenuId = rIdent.toUInt32();

    NavigatorDragType eDT = static_cast<NavigatorDragType>(nMenuId);
    if( meDragType == eDT )
        return;

    meDragType = eDT;
    SetDragImage();

    if( meDragType == NAVIGATOR_DRAGTYPE_URL )
    {
        // patch, prevents endless loop
        if (mxTlbObjects->count_selected_rows() > 1)
            mxTlbObjects->unselect_all();

        mxTlbObjects->set_selection_mode(SelectionMode::Single);
    }
    else
        mxTlbObjects->set_selection_mode(SelectionMode::Multiple);
}

IMPL_LINK( SdNavigatorWin, ShapeFilterCallback, const OUString&, rIdent, void )
{
    bool bShowAllShapes(mxTlbObjects->GetShowAllShapes());
    bool bOrderFrontToBack(mxTlbObjects->GetOrderFrontToBack());
    if (rIdent == "named")
        bShowAllShapes = false;
    else if (rIdent == "all")
        bShowAllShapes = true;
    else if (rIdent == "fronttoback")
        bOrderFrontToBack = true;
    else if (rIdent == "backtofront")
        bOrderFrontToBack = false;
    else
        OSL_FAIL("SdNavigatorWin::ShapeFilterCallback called for unknown menu entry");

    mxTlbObjects->SetOrderFrontToBack(bOrderFrontToBack);
    mxTlbObjects->SetShowAllShapes(bShowAllShapes, true);

    // Remember the selection in the FrameView.
    NavDocInfo* pInfo = GetDocInfo();
    if (pInfo == nullptr)
        return;

    ::sd::DrawDocShell* pDocShell = pInfo->mpDocShell;
    if (pDocShell != nullptr)
    {
        ::sd::ViewShell* pViewShell = pDocShell->GetViewShell();
        if (pViewShell != nullptr)
        {
            ::sd::FrameView* pFrameView = pViewShell->GetFrameView();
            if (pFrameView != nullptr)
            {
                pFrameView->SetIsNavigatorShowingAllShapes(bShowAllShapes);
            }
            lcl_select_marked_objects(pViewShell, mxTlbObjects.get());
        }
    }
}

void SdNavigatorWin::RefreshDocumentLB( const OUString* pDocName )
{
    sal_Int32 nPos = 0;

    if( pDocName )
    {
        if( mbDocImported )
            mxLbDocs->remove(0);

        mxLbDocs->insert_text(0, *pDocName);
        mbDocImported = true;
    }
    else
    {
        nPos = mxLbDocs->get_active();
        if (nPos == -1)
            nPos = 0;

        OUString aStr;
        if( mbDocImported )
            aStr = mxLbDocs->get_text(0);

        mxLbDocs->clear();

        // delete list of DocInfos
        maDocList.clear();

        if( mbDocImported )
            mxLbDocs->insert_text(0, aStr);

        ::sd::DrawDocShell* pCurrentDocShell =
              dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() );
        SfxObjectShell* pSfxDocShell = SfxObjectShell::GetFirst();
        while( pSfxDocShell )
        {
            ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( pSfxDocShell );
            if( pDocShell  && !pDocShell->IsInDestruction() && ( pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) )
            {
                NavDocInfo aInfo ;
                aInfo.mpDocShell = pDocShell;

                SfxMedium *pMedium = pDocShell->GetMedium();
                aStr = pMedium ? pMedium->GetName() : OUString();
                if( !aStr.isEmpty() )
                    aInfo.SetName( true );
                else
                    aInfo.SetName( false );
                // at the moment, we use the name of the shell again (i.e.
                // without path) since Koose thinks it is an error if the path
                // is shown in url notation!
                aStr = pDocShell->GetName();

                mxLbDocs->append_text(aStr);

                if( pDocShell == pCurrentDocShell )
                    aInfo.SetActive( true );
                else
                    aInfo.SetActive( false );

                maDocList.push_back( aInfo );
            }
            pSfxDocShell = SfxObjectShell::GetNext(*pSfxDocShell);
        }
    }
    mxLbDocs->set_active(nPos);
}

const OUString & SdNavigatorWin::GetDragTypeSdBmpId(NavigatorDragType eDT)
{
    switch( eDT )
    {
        case NAVIGATOR_DRAGTYPE_NONE:
                return EMPTY_OUSTRING;
        case NAVIGATOR_DRAGTYPE_URL:
                return BMP_HYPERLINK;
        case NAVIGATOR_DRAGTYPE_EMBEDDED:
                return BMP_EMBEDDED;
        case NAVIGATOR_DRAGTYPE_LINK:
                return BMP_LINK;
        default: OSL_FAIL( "No resource for DragType available!" );
    }
    return EMPTY_OUSTRING;
}

NavDocInfo* SdNavigatorWin::GetDocInfo()
{
    sal_uInt32 nPos = mxLbDocs->get_active();

    if( mbDocImported )
    {
        if( nPos == 0 )
        {
            return nullptr;
        }
        nPos--;
    }

    return nPos < maDocList.size() ? &(maDocList[ nPos ]) : nullptr;
}

void SdNavigatorWin::SetDragImage()
{
    mxToolbox->set_item_icon_name(u"dragmode"_ustr, GetDragTypeSdBmpId(meDragType));
}

// for the sidebar to make the panel invisible when the shell type is outline or slide sorter
void SdNavigatorWin::HandleContextChange(const vcl::EnumContext& eContext)
{
    if (eContext.GetApplication() != vcl::EnumContext::Application::Impress)
        return;

    ::sd::DrawDocShell* pCurrentDocShell
            = dynamic_cast<::sd::DrawDocShell*>(SfxObjectShell::Current());
    if (!pCurrentDocShell)
        return;

    const ::sd::DrawViewShell* pDrawViewShell
            = static_cast<::sd::DrawViewShell*>(pCurrentDocShell->GetViewShell());
    if (!pDrawViewShell)
        return;

    sd::ViewShell::ShellType eShellType = pDrawViewShell->GetShellType();
    m_xContainer->set_visible(eShellType != sd::ViewShell::ST_OUTLINE
            && eShellType != sd::ViewShell::ST_SLIDE_SORTER);
}

/**
 * ControllerItem for Navigator
 */
SdNavigatorControllerItem::SdNavigatorControllerItem(
    sal_uInt16 _nId,
    SdNavigatorWin* pNavWin,
    SfxBindings*    _pBindings,
    SdNavigatorWin::UpdateRequestFunctor aUpdateRequest)
    : SfxControllerItem( _nId, *_pBindings ),
      pNavigatorWin( pNavWin ),
      maUpdateRequest(std::move(aUpdateRequest))
{
}

void SdNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId,
                        SfxItemState eState, const SfxPoolItem* pItem )
{
    if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_STATE )
        return;

    // only if doc in LB is the active
    NavDocInfo* pInfo = pNavigatorWin->GetDocInfo();
    if( !(pInfo && pInfo->IsActive()) )
        return;

    const SfxUInt32Item& rStateItem = dynamic_cast<const SfxUInt32Item&>(*pItem);
    NavState nState = static_cast<NavState>(rStateItem.GetValue());

    // First
    if (nState & NavState::BtnFirstEnabled &&
        !pNavigatorWin->mxToolbox->get_item_sensitive(u"first"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"first"_ustr, true);
    if (nState & NavState::BtnFirstDisabled &&
        pNavigatorWin->mxToolbox->get_item_sensitive(u"first"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"first"_ustr, false);

    // Prev
    if (nState & NavState::BtnPrevEnabled &&
        !pNavigatorWin->mxToolbox->get_item_sensitive(u"previous"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"previous"_ustr, true);
    if (nState & NavState::BtnPrevDisabled &&
        pNavigatorWin->mxToolbox->get_item_sensitive(u"previous"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"previous"_ustr, false);

    // Last
    if (nState & NavState::BtnLastEnabled &&
        !pNavigatorWin->mxToolbox->get_item_sensitive(u"last"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"last"_ustr, true);
    if (nState & NavState::BtnLastDisabled &&
        pNavigatorWin->mxToolbox->get_item_sensitive(u"last"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"last"_ustr, false);

    // Next
    if (nState & NavState::BtnNextEnabled &&
        !pNavigatorWin->mxToolbox->get_item_sensitive(u"next"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"next"_ustr, true);
    if (nState & NavState::BtnNextDisabled &&
        pNavigatorWin->mxToolbox->get_item_sensitive(u"next"_ustr))
        pNavigatorWin->mxToolbox->set_item_sensitive(u"next"_ustr, false);

    if (nState & NavState::TableUpdate)
    {
        // InitTlb; is initiated by Slot
        if (maUpdateRequest)
            maUpdateRequest();
    }
}

/**
 * ControllerItem for Navigator to show page in TreeLB
 */
SdPageNameControllerItem::SdPageNameControllerItem(
    sal_uInt16 _nId,
    SdNavigatorWin* pNavWin,
    SfxBindings*    _pBindings)
    : SfxControllerItem( _nId, *_pBindings ),
      pNavigatorWin( pNavWin )
{
}

void SdPageNameControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId,
                        SfxItemState eState, const SfxPoolItem* pItem )
{
    if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_PAGENAME )
        return;

    // only if doc in LB is the active
    NavDocInfo* pInfo = pNavigatorWin->GetDocInfo();
    if( !(pInfo && pInfo->IsActive()) )
        return;

    // Without a test for marked objects the page name entry is not selected when there are no
    // marked objects. The HasSelectedChildren test is required when in 'Named Shapes' mode in
    // order to select the page name when none of the marked objects have a name.
    bool bDrawViewHasMarkedObjects = false;
    if (pInfo->GetDrawDocShell() && pInfo->GetDrawDocShell()->GetViewShell())
    {
        const SdrView* pDrawView = pInfo->GetDrawDocShell()->GetViewShell()->GetDrawView();
        if (pDrawView && pDrawView->GetMarkedObjectList().GetMarkCount())
            bDrawViewHasMarkedObjects = true;
    }

    const SfxStringItem& rStateItem = dynamic_cast<const SfxStringItem&>(*pItem);
    const OUString& aPageName = rStateItem.GetValue();

    if (!bDrawViewHasMarkedObjects || !pNavigatorWin->mxTlbObjects->HasSelectedChildren(aPageName))
    {
        if (pNavigatorWin->mxTlbObjects->get_selection_mode() == SelectionMode::Multiple)
        {
            // because otherwise it is always additional select
            pNavigatorWin->mxTlbObjects->unselect_all();
        }
        pNavigatorWin->mxTlbObjects->SelectEntry( aPageName );
    }
}

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