    /* -*- 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/types.h>
    #include <sal/log.hxx>
    #include <sot/formats.hxx>
    #include <sot/storage.hxx>
    #include <vcl/weld.hxx>
    #include <svl/urihelper.hxx>
    #include <svx/svditer.hxx>
    #include <sfx2/docfile.hxx>
    #include <svx/svdoole2.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/builderfactory.hxx>
    #include <cusshow.hxx>
    #include <sfx2/childwin.hxx>

    #include <sfx2/viewfrm.hxx>

    #include <strmname.h>
    #include <sdtreelb.hxx>
    #include <DrawDocShell.hxx>
    #include <drawdoc.hxx>
    #include <sdpage.hxx>
    #include <sdmod.hxx>
    #include <sdresid.hxx>
    #include <navigatr.hxx>
    #include <strings.hrc>

    #include <bitmaps.hlst>
    #include <customshowlist.hxx>
    #include <ViewShell.hxx>
    #include <DrawController.hxx>
    #include <ViewShellBase.hxx>

    #include <com/sun/star/embed/XEmbedPersist.hpp>
    #include <com/sun/star/frame/Desktop.hpp>
    #include <com/sun/star/frame/XFramesSupplier.hpp>
    #include <svtools/acceleratorexecute.hxx>
    #include <svtools/embedtransfer.hxx>
    #include <svtools/svlbitm.hxx>
    #include <svtools/treelistentry.hxx>
    #include <comphelper/servicehelper.hxx>
    #include <comphelper/processfactory.hxx>
    #include <tools/diagnose_ex.h>
    #include <o3tl/make_unique.hxx>
    #include <comphelper/scopeguard.hxx>

    using namespace com::sun::star;

    class SdPageObjsTLB::IconProvider
    {
    public:
        IconProvider();

        // Regular icons.
        Image maImgPage;
        Image maImgPageExcl;
        Image maImgPageObjsExcl;
        Image maImgPageObjs;
        Image maImgObjects;
        Image maImgGroup;
    };

    bool SdPageObjsTLB::bIsInDrag = false;

    bool SdPageObjsTLB::IsInDrag()
    {
        return bIsInDrag;
    }

    SotClipboardFormatId SdPageObjsTLB::SdPageObjsTransferable::mnListBoxDropFormatId = static_cast<SotClipboardFormatId>(SAL_MAX_UINT32);

    SdPageObjsTLB::SdPageObjsTransferable::SdPageObjsTransferable(
        SdPageObjsTLB& rParent,
            const INetBookmark& rBookmark,
        ::sd::DrawDocShell& rDocShell,
        NavigatorDragType eDragType)
        : SdTransferable(rDocShell.GetDoc(), nullptr, true),
          mrParent( rParent ),
          maBookmark( rBookmark ),
          mrDocShell( rDocShell ),
          meDragType( eDragType )
    {
        rParent.SetupDragOrigin();
    }

    VCL_BUILDER_FACTORY_CONSTRUCTOR(SdPageObjsTLB, WB_TABSTOP)

    SdPageObjsTLB::SdPageObjsTransferable::~SdPageObjsTransferable()
    {
    }

    void SdPageObjsTLB::SdPageObjsTransferable::AddSupportedFormats()
    {
        AddFormat(SotClipboardFormatId::NETSCAPE_BOOKMARK);
        AddFormat(SotClipboardFormatId::TREELISTBOX);
        AddFormat(GetListBoxDropFormatId());
    }

    bool SdPageObjsTLB::SdPageObjsTransferable::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
    {
        SotClipboardFormatId nFormatId = SotExchange::GetFormat( rFlavor );
        switch (nFormatId)
        {
            case SotClipboardFormatId::NETSCAPE_BOOKMARK:
                SetINetBookmark( maBookmark, rFlavor );
                return true;

            case SotClipboardFormatId::TREELISTBOX:
            {
                css::uno::Any aTreeListBoxData; // empty for now
                SetAny(aTreeListBoxData);
                return true;
            }

            default:
                return false;
        }
    }

    void SdPageObjsTLB::SdPageObjsTransferable::DragFinished( sal_Int8 nDropAction )
    {
        mrParent.OnDragFinished();
        SdTransferable::DragFinished(nDropAction);
    }

    sal_Int64 SAL_CALL SdPageObjsTLB::SdPageObjsTransferable::getSomething( const css::uno::Sequence< sal_Int8 >& rId )
    {
        sal_Int64 nRet;

        if( ( rId.getLength() == 16 ) &&
            ( 0 == memcmp( getUnoTunnelId().getConstArray(), rId.getConstArray(), 16 ) ) )
        {
            nRet = static_cast<sal_Int64>(reinterpret_cast<sal_IntPtr>(this));
        }
        else
            nRet = SdTransferable::getSomething(rId);

        return nRet;
    }

    namespace
    {
        class theSdPageObjsTLBUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSdPageObjsTLBUnoTunnelId > {};
    }

    const css::uno::Sequence< sal_Int8 >& SdPageObjsTLB::SdPageObjsTransferable::getUnoTunnelId()
    {
        return theSdPageObjsTLBUnoTunnelId::get().getSeq();
    }

    SdPageObjsTLB::SdPageObjsTransferable* SdPageObjsTLB::SdPageObjsTransferable::getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData )
        throw()
    {
        try
        {
            css::uno::Reference< css::lang::XUnoTunnel > xUnoTunnel( rxData, css::uno::UNO_QUERY_THROW );

            return reinterpret_cast<SdPageObjsTLB::SdPageObjsTransferable*>(
                    sal::static_int_cast<sal_uIntPtr>(
                        xUnoTunnel->getSomething( SdPageObjsTLB::SdPageObjsTransferable::getUnoTunnelId()) ) );
        }
        catch( const css::uno::Exception& )
        {
        }
        return nullptr;
    }

    SotClipboardFormatId SdPageObjsTLB::SdPageObjsTransferable::GetListBoxDropFormatId()
    {
        if (mnListBoxDropFormatId == static_cast<SotClipboardFormatId>(SAL_MAX_UINT32))
            mnListBoxDropFormatId = SotExchange::RegisterFormatMimeType("application/x-openoffice-treelistbox-moveonly;windows_formatname=\"SV_LBOX_DD_FORMAT_MOVE\"");
        return mnListBoxDropFormatId;
    }

    SdPageObjsTLB::SdPageObjsTLB( vcl::Window* pParentWin, WinBits nStyle )
    :   SvTreeListBox       ( pParentWin, nStyle )
    ,   bisInSdNavigatorWin ( false )
    ,   mpParent            ( pParentWin )
    ,   mpDoc               ( nullptr )
    ,   mpBookmarkDoc       ( nullptr )
    ,   mpMedium            ( nullptr )
    ,   mpOwnMedium         ( nullptr )
    ,   maImgOle            ( BitmapEx(BMP_OLE) )
    ,   maImgGraphic        ( BitmapEx(BMP_GRAPHIC) )
    ,   mbLinkableSelected  ( false )
    ,   mpDropNavWin        ( nullptr )
    ,   mpFrame             ( nullptr )
    ,   mbSaveTreeItemState ( false )
    ,   mbShowAllShapes     ( false )
    ,   mbShowAllPages      ( false )
    ,   mbSelectionHandlerNavigates(false)
    ,   mbNavigationGrabsFocus(true)
    {
        // add lines to Tree-ListBox
        SetStyle( GetStyle() | WB_TABSTOP | WB_BORDER | WB_HASLINES |
                               WB_HASBUTTONS | // WB_HASLINESATROOT |
                               WB_HSCROLL |
                               WB_HASBUTTONSATROOT );
        SetQuickSearch(true); /* i31275 */;
        SetNodeBitmaps(Image(BitmapEx(BMP_EXPAND)), Image(BitmapEx(BMP_COLLAPSE)));

        SetDragDropMode(
             DragDropMode::CTRL_MOVE | DragDropMode::CTRL_COPY |
                DragDropMode::APP_MOVE  | DragDropMode::APP_COPY  | DragDropMode::APP_DROP );

        m_pAccel = ::svt::AcceleratorExecute::createAcceleratorHelper();
    }

    void SdPageObjsTLB::SetViewFrame( SfxViewFrame* pViewFrame )
    {
        mpFrame = pViewFrame;
        sd::ViewShellBase* pBase = sd::ViewShellBase::GetViewShellBase(pViewFrame);
        const css::uno::Reference< css::frame::XFrame > xFrame = pBase->GetMainViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
        m_pAccel->init(::comphelper::getProcessComponentContext(), xFrame);
    }

    SdPageObjsTLB::~SdPageObjsTLB()
    {
        disposeOnce();
    }

    void SdPageObjsTLB::dispose()
    {
        if ( mpBookmarkDoc )
            CloseBookmarkDoc();
        else
            // no document was created from mpMedium, so this object is still the owner of it
            delete mpMedium;
        mpParent.clear();
        mpDropNavWin.clear();
        m_pAccel.reset();
        SvTreeListBox::dispose();
    }

    // helper function for  GetEntryAltText and GetEntryLongDescription
    OUString SdPageObjsTLB::getAltLongDescText(SvTreeListEntry* pEntry , bool isAltText) const
    {
        sal_uInt16 maxPages = mpDoc->GetPageCount();
        sal_uInt16 pageNo;
        SdrObject*   pObj = nullptr;

        OUString ParentName = GetEntryText( GetRootLevelParent( pEntry ) );

        for( pageNo = 0;  pageNo < maxPages; pageNo++ )
        {
            const SdPage* pPage = static_cast<const SdPage*>( mpDoc->GetPage( pageNo ) );
            if( pPage->GetPageKind() != PageKind::Standard ) continue;
            if( pPage->GetName() !=  ParentName ) continue;
            SdrObjListIter aIter( pPage, SdrIterMode::Flat );
            while( aIter.IsMore() )
            {
                pObj = aIter.Next();
                if( GetEntryText(pEntry) ==  GetObjectName( pObj )  )
                {
                    if( isAltText )
                        return pObj->GetTitle();
                    else
                        return pObj->GetDescription();
                }
            }
        }
        return OUString();

    }

    OUString SdPageObjsTLB::GetEntryAltText( SvTreeListEntry* pEntry ) const
    {
        return getAltLongDescText( pEntry, true );
    }

    OUString SdPageObjsTLB::GetEntryLongDescription( SvTreeListEntry* pEntry ) const
    {
        return getAltLongDescText( pEntry, false);
    }

    void SdPageObjsTLB::InitEntry(SvTreeListEntry* pEntry,
        const OUString& rStr, const Image& rImg1, const Image& rImg2, SvLBoxButtonKind eButtonKind)
    {
        sal_uInt16 nColToHilite = 1; //0==Bitmap;1=="Spalte1";2=="Spalte2"
        SvTreeListBox::InitEntry( pEntry, rStr, rImg1, rImg2, eButtonKind );
        SvLBoxString& rCol = static_cast<SvLBoxString&>(pEntry->GetItem( nColToHilite ));
        pEntry->ReplaceItem(o3tl::make_unique<SvLBoxString>(rCol.GetText()), nColToHilite );
    }

    void SdPageObjsTLB::SaveExpandedTreeItemState(SvTreeListEntry* pEntry, std::vector<OUString>& vectTreeItem)
    {
        if (pEntry)
        {
            SvTreeListEntry* pListEntry = pEntry;
            while (pListEntry)
            {
                if (pListEntry->HasChildren())
                {
                    if (IsExpanded(pListEntry))
                        vectTreeItem.push_back(GetEntryText(pListEntry));
                    SvTreeListEntry* pChildEntry = FirstChild(pListEntry);
                    SaveExpandedTreeItemState(pChildEntry, vectTreeItem);
                }
                pListEntry = pListEntry->NextSibling();
            }
        }
    }
    void SdPageObjsTLB::Clear()
    {
        //Save the expanded tree item
        if (mbSaveTreeItemState)
        {
            maSelectionEntryText.clear();
            maTreeItem.clear();
            if (GetCurEntry())
                maSelectionEntryText = GetSelectedEntry();
            SvTreeListEntry* pEntry = FirstChild(nullptr);
            SaveExpandedTreeItemState(pEntry, maTreeItem);
        }
        return SvTreeListBox::Clear();
    }

    OUString SdPageObjsTLB::GetObjectName(
        const SdrObject* pObject,
        const bool bCreate) const
    {
        OUString aRet;

        if ( pObject )
        {
            aRet = pObject->GetName();

            if (aRet.isEmpty() && dynamic_cast<const SdrOle2Obj* >(pObject) !=  nullptr)
                aRet = static_cast< const SdrOle2Obj* >( pObject )->GetPersistName();
        }

        if (bCreate
            && mbShowAllShapes
            && aRet.isEmpty()
            && pObject!=nullptr)
        {
            aRet = SdResId(STR_NAVIGATOR_SHAPE_BASE_NAME);
            aRet = aRet.replaceFirst("%1", OUString::number(pObject->GetOrdNum() + 1));
        }

        return aRet;
    }

    /**
     * select a entry in TreeLB
     */
    bool SdPageObjsTLB::SelectEntry( const OUString& rName )
    {
        bool bFound = false;

        if( !rName.isEmpty() )
        {
            SvTreeListEntry* pEntry = nullptr;
            OUString aTmp;

            for( pEntry = First(); pEntry && !bFound; pEntry = Next( pEntry ) )
            {
                aTmp = GetEntryText( pEntry );
                if( aTmp == rName )
                {
                    bFound = true;
                    SetCurEntry( pEntry );
                }
            }
        }
        return bFound;
    }

    /**
     * @return true if children of the specified string are selected
     */
    bool SdPageObjsTLB::HasSelectedChildren( const OUString& rName )
    {
        bool bChildren = false;

        if( !rName.isEmpty() )
        {
            bool bFound  = false;
            SvTreeListEntry* pEntry = nullptr;
            OUString aTmp;

            for( pEntry = First(); pEntry && !bFound; pEntry = Next( pEntry ) )
            {
                aTmp = GetEntryText( pEntry );
                if( aTmp == rName )
                {
                    bFound = true;
                    bool bExpanded = IsExpanded( pEntry );
                    long nCount = GetChildSelectionCount( pEntry );
                    if( bExpanded && nCount > 0 )
                        bChildren = true;
                }
            }
        }
        return bChildren;
    }

    /**
     * Fill TreeLB with pages and objects
     */
    void SdPageObjsTLB::Fill( const SdDrawDocument* pInDoc, bool bAllPages,
                              const OUString& rDocName)
    {
        OUString aSelection;
        if( GetSelectionCount() > 0 )
        {
            aSelection = GetSelectedEntry();
            Clear();
        }

        mpDoc = pInDoc;
        maDocName = rDocName;
        mbShowAllPages = bAllPages;
        mpMedium = nullptr;

        IconProvider aIconProvider;

        // first insert all pages including objects
        sal_uInt16 nPage = 0;
        const sal_uInt16 nMaxPages = mpDoc->GetPageCount();

        while( nPage < nMaxPages )
        {
            const SdPage* pPage = static_cast<const SdPage*>( mpDoc->GetPage( nPage ) );
            if(  (mbShowAllPages || pPage->GetPageKind() == PageKind::Standard)
                 && (pPage->GetPageKind() != PageKind::Handout)   ) //#94954# never list the normal handout page ( handout-masterpage is used instead )
            {
                bool bPageExluded = pPage->IsExcluded();

                bool bPageBelongsToShow = PageBelongsToCurrentShow (pPage);
                bPageExluded |= !bPageBelongsToShow;

                AddShapeList(*pPage, nullptr, pPage->GetName(), bPageExluded, nullptr, aIconProvider);
            }
            nPage++;
        }

        // then insert all master pages including objects
        if( mbShowAllPages )
        {
            nPage = 0;
            const sal_uInt16 nMaxMasterPages = mpDoc->GetMasterPageCount();

            while( nPage < nMaxMasterPages )
            {
                const SdPage* pPage = static_cast<const SdPage*>( mpDoc->GetMasterPage( nPage ) );
                AddShapeList(*pPage, nullptr, pPage->GetName(), false, nullptr, aIconProvider);
                nPage++;
            }
        }
        if( !aSelection.isEmpty() )
            SelectEntry( aSelection );
        else if (mbSaveTreeItemState && !maSelectionEntryText.isEmpty())
        {
            SelectEntry(maSelectionEntryText);
        }
    }

    /**
     * We insert only the first entry. Children are created on demand.
     */
    void SdPageObjsTLB::Fill( const SdDrawDocument* pInDoc, SfxMedium* pInMedium,
                              const OUString& rDocName )
    {
        mpDoc = pInDoc;

        // this object now owns the Medium
        mpMedium = pInMedium;
        maDocName = rDocName;

        Image aImgDocOpen=Image(BitmapEx(BMP_DOC_OPEN));
        Image aImgDocClosed=Image(BitmapEx(BMP_DOC_CLOSED));

        // insert document name
        InsertEntry( maDocName, aImgDocOpen, aImgDocClosed, nullptr, true, TREELIST_APPEND,
                     reinterpret_cast< void* >( 1 )
        );
    }

    void SdPageObjsTLB::AddShapeList (
        const SdrObjList& rList,
        SdrObject* pShape,
        const OUString& rsName,
        const bool bIsExcluded,
        SvTreeListEntry* pParentEntry,
        const IconProvider& rIconProvider)
    {
        Image aIcon (rIconProvider.maImgPage);
        if (bIsExcluded)
            aIcon = rIconProvider.maImgPageExcl;
        else if (pShape != nullptr)
            aIcon = rIconProvider.maImgGroup;

        void* pUserData (reinterpret_cast<void*>(1));
        if (pShape != nullptr)
            pUserData = pShape;

        SvTreeListEntry* pEntry = InsertEntry(
            rsName,
            aIcon,
            aIcon,
            pParentEntry,
            false,
            TREELIST_APPEND,
            pUserData);

        SdrObjListIter aIter(
            &rList,
            !rList.HasObjectNavigationOrder() /* use navigation order, if available */,
            SdrIterMode::Flat);

        while( aIter.IsMore() )
        {
            SdrObject* pObj = aIter.Next();
            OSL_ASSERT(pObj!=nullptr);

            // Get the shape name.
            OUString aStr (GetObjectName( pObj ) );

            if( !aStr.isEmpty() )
            {
                if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == OBJ_OLE2 )
                {
                    InsertEntry(
                        aStr,
                        maImgOle,
                        maImgOle,
                        pEntry,
                        false,
                        TREELIST_APPEND,
                        pObj
                    );
                }
                else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == OBJ_GRAF )
                {
                    InsertEntry(
                        aStr,
                        maImgGraphic,
                        maImgGraphic,
                        pEntry,
                        false,
                        TREELIST_APPEND,
                        pObj
                    );
                }
                else if (pObj->IsGroupObject())
                {
                    AddShapeList(
                        *pObj->GetSubList(),
                        pObj,
                        aStr,
                        false,
                        pEntry,
                        rIconProvider
                    );
                }
                else
                {
                    InsertEntry(
                        aStr,
                        rIconProvider.maImgObjects,
                        rIconProvider.maImgObjects,
                        pEntry,
                        false,
                        TREELIST_APPEND,
                        pObj
                    );
                }
            }
        }

        if( pEntry->HasChildren() )
        {
            SetExpandedEntryBmp(
                pEntry,
                bIsExcluded ? rIconProvider.maImgPageObjsExcl : rIconProvider.maImgPageObjs);
            SetCollapsedEntryBmp(
                pEntry,
                bIsExcluded ? rIconProvider.maImgPageObjsExcl : rIconProvider.maImgPageObjs);
            if (mbSaveTreeItemState)
            {
                std::vector<OUString>::iterator iteStart = maTreeItem.begin();
                while (iteStart != maTreeItem.end())
                {
                    OUString strEntry = GetEntryText(pEntry);
                    if (*iteStart == strEntry)
                    {
                        Expand( pEntry );
                        break;
                    }
                    ++iteStart;
                }
            }
            else
                Expand( pEntry );
        }
    }

    void SdPageObjsTLB::SetShowAllShapes (
        const bool bShowAllShapes,
        const bool bFillList)
    {
        mbShowAllShapes = bShowAllShapes;
        if (bFillList)
        {
            if (mpMedium == nullptr)
                Fill(mpDoc, mbShowAllPages, maDocName);
            else
                Fill(mpDoc, mpMedium, maDocName);
        }
    }

    bool SdPageObjsTLB::IsEqualToShapeList(SvTreeListEntry*& pEntry, const SdrObjList& rList,
                                           const OUString& rListName)
    {
        if (!pEntry)
            return false;
        OUString aName = GetEntryText(pEntry);

        if (rListName != aName)
            return false;

        pEntry = Next(pEntry);

        SdrObjListIter aIter(&rList,
                             !rList.HasObjectNavigationOrder() /* use navigation order, if available */,
                             SdrIterMode::Flat);

        while (aIter.IsMore())
        {
            SdrObject* pObj = aIter.Next();

            const OUString aObjectName(GetObjectName(pObj));

            if (!aObjectName.isEmpty())
            {
                if (!pEntry)
                    return false;

                aName = GetEntryText(pEntry);

                if (aObjectName != aName)
                    return false;

                if (pObj->IsGroupObject())
                {
                    bool bRet = IsEqualToShapeList(pEntry, *pObj->GetSubList(), aObjectName);
                    if (!bRet)
                        return false;
                }
                else
                    pEntry = Next(pEntry);
            }
        }

        return true;
    }

    /**
     * Checks if the pages (PageKind::Standard) of a doc and the objects on the pages
     * are identical to the TreeLB.
     * If a doc is provided, this will be the used doc (important by more than
     * one document).
     */
    bool SdPageObjsTLB::IsEqualToDoc( const SdDrawDocument* pInDoc )
    {
        if( pInDoc )
            mpDoc = pInDoc;

        if( !mpDoc )
            return false;

        SvTreeListEntry* pEntry = First();

        // compare all pages including the objects
        sal_uInt16 nPage = 0;
        const sal_uInt16 nMaxPages = mpDoc->GetPageCount();

        while( nPage < nMaxPages )
        {
            const SdPage* pPage = static_cast<const SdPage*>( mpDoc->GetPage( nPage ) );
            if( pPage->GetPageKind() == PageKind::Standard )
            {
                bool bRet = IsEqualToShapeList(pEntry, *pPage, pPage->GetName());
                if (!bRet)
                    return false;
            }
            nPage++;
        }
        // If there are still entries in the listbox,
        // then objects (with names) or pages were deleted
        return !pEntry;
    }

    /**
     * @return selected string
     */
    OUString SdPageObjsTLB::GetSelectedEntry()
    {
        return GetEntryText( GetCurEntry() );
    }

    std::vector<OUString> SdPageObjsTLB::GetSelectEntryList( const sal_uInt16 nDepth ) const
    {
        std::vector<OUString> aEntries;
        SvTreeListEntry* pEntry = FirstSelected();

        while( pEntry )
        {
            sal_uInt16 nListDepth = GetModel()->GetDepth( pEntry );

            if( nListDepth == nDepth )
                aEntries.push_back(GetEntryText(pEntry));

            pEntry = NextSelected( pEntry );
        }

        return aEntries;
    }

    /**
     * Entries are inserted only by request (double click)
     */
    void SdPageObjsTLB::RequestingChildren( SvTreeListEntry* pFileEntry )
    {
        if( !pFileEntry->HasChildren() )
        {
            if( GetBookmarkDoc() )
            {
                SdrObject*   pObj = nullptr;
                SvTreeListEntry* pPageEntry = nullptr;

                Image aImgPage     = Image(BitmapEx(BMP_PAGE));
                Image aImgPageObjs = Image(BitmapEx(BMP_PAGEOBJS));
                Image aImgObjects  = Image(BitmapEx(BMP_OBJECTS));

                // document name already inserted

                // only insert all "normal" ? slides with objects
                sal_uInt16 nPage = 0;
                const sal_uInt16 nMaxPages = mpBookmarkDoc->GetPageCount();

                while( nPage < nMaxPages )
                {
                    SdPage* pPage = static_cast<SdPage*>( mpBookmarkDoc->GetPage( nPage ) );
                    if( pPage->GetPageKind() == PageKind::Standard )
                    {
                        pPageEntry = InsertEntry( pPage->GetName(),
                                                  aImgPage,
                                                  aImgPage,
                                                  pFileEntry,
                                                  false,
                                                  TREELIST_APPEND,
                                                  reinterpret_cast< void* >( 1 ) );

                        SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );

                        while( aIter.IsMore() )
                        {
                            pObj = aIter.Next();
                            OUString aStr( GetObjectName( pObj ) );
                            if( !aStr.isEmpty() )
                            {
                                if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == OBJ_OLE2 )
                                {
                                    InsertEntry(aStr, maImgOle, maImgOle, pPageEntry);
                                }
                                else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == OBJ_GRAF )
                                {
                                    InsertEntry(aStr, maImgGraphic, maImgGraphic, pPageEntry);
                                }
                                else
                                {
                                    InsertEntry(aStr, aImgObjects, aImgObjects, pPageEntry);
                                }
                            }
                        }
                        if( pPageEntry->HasChildren() )
                        {
                            SetExpandedEntryBmp(  pPageEntry, aImgPageObjs );
                            SetCollapsedEntryBmp( pPageEntry, aImgPageObjs );
                        }
                    }
                    nPage++;
                }
            }
        }
        else
            SvTreeListBox::RequestingChildren( pFileEntry );
    }

    /**
     * Checks if it is a draw file and opens the BookmarkDoc depending of
     * the provided Docs
     */
    SdDrawDocument* SdPageObjsTLB::GetBookmarkDoc(SfxMedium* pMed)
    {
        if (
           !mpBookmarkDoc ||
             (pMed && (!mpOwnMedium || mpOwnMedium->GetName() != pMed->GetName()))
          )
        {
            // create a new BookmarkDoc if now one exists or if a new Medium is provided
            if (mpOwnMedium != pMed)
            {
                CloseBookmarkDoc();
            }

            if (pMed)
            {
                // it looks that it is undefined if a Medium was set by Fill() already
                DBG_ASSERT( !mpMedium, "SfxMedium confusion!" );
                delete mpMedium;
                mpMedium = nullptr;

                // take over this Medium (currently used only be Navigator)
                mpOwnMedium = pMed;
            }

            DBG_ASSERT( mpMedium || pMed, "No SfxMedium provided!" );

            if( pMed )
            {
                // in this mode the document is also owned and controlled by this instance
                mxBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress);
                if (mxBookmarkDocShRef->DoLoad(pMed))
                    mpBookmarkDoc = mxBookmarkDocShRef->GetDoc();
                else
                    mpBookmarkDoc = nullptr;
            }
            else if ( mpMedium )
                // in this mode the document is owned and controlled by the SdDrawDocument
                // it can be released by calling the corresponding CloseBookmarkDoc method
                // successful creation of a document makes this the owner of the medium
                mpBookmarkDoc = const_cast<SdDrawDocument*>(mpDoc)->OpenBookmarkDoc(mpMedium);

            if ( !mpBookmarkDoc )
            {
                std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(GetFrameWeld(),
                                                               VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR)));
                xErrorBox->run();
                mpMedium = nullptr; //On failure the SfxMedium is invalid
            }
        }

        return mpBookmarkDoc;
    }

    /**
     * Close and delete bookmark document
     */
    void SdPageObjsTLB::CloseBookmarkDoc()
    {
        if (mxBookmarkDocShRef.is())
        {
            mxBookmarkDocShRef->DoClose();
            mxBookmarkDocShRef.clear();

            // Medium is owned by document, so it's destroyed already
            mpOwnMedium = nullptr;
        }
        else if ( mpBookmarkDoc )
        {
            DBG_ASSERT( !mpOwnMedium, "SfxMedium confusion!" );
            if ( mpDoc )
            {
                // The document owns the Medium, so the Medium will be invalid after closing the document
                const_cast<SdDrawDocument*>(mpDoc)->CloseBookmarkDoc();
                mpMedium = nullptr;
            }
        }
        else
        {
            // perhaps mpOwnMedium provided, but no successful creation of BookmarkDoc
            delete mpOwnMedium;
            mpOwnMedium = nullptr;
        }

        mpBookmarkDoc = nullptr;
    }

    void SdPageObjsTLB::SelectHdl()
    {
        SvTreeListEntry* pEntry = FirstSelected();

        mbLinkableSelected = true;

        while( pEntry && mbLinkableSelected )
        {
            if( nullptr == pEntry->GetUserData() )
                mbLinkableSelected = false;

            pEntry = NextSelected( pEntry );
        }

        SvTreeListBox::SelectHdl();

        if (mbSelectionHandlerNavigates)
            DoubleClickHdl();
    }

    /**
     * Overloads RETURN with the functionality of DoubleClick
     */
    void SdPageObjsTLB::KeyInput( const KeyEvent& rKEvt )
    {
        const vcl::KeyCode& aKeyCode = rKEvt.GetKeyCode();
        if ( m_pAccel->execute( aKeyCode ) )
            // the accelerator consumed the event
            return;
        if( rKEvt.GetKeyCode().GetCode() == KEY_RETURN )
        {
            // commented code from svtools/source/contnr/svimpbox.cxx
            SvTreeListEntry* pCursor = GetCurEntry();
            if (!pCursor)
                return;
            if( pCursor->HasChildren() || pCursor->HasChildrenOnDemand() )
            {
                if( IsExpanded( pCursor ) )
                    Collapse( pCursor );
                else
                    Expand( pCursor );
            }

            DoubleClickHdl();
        }
        else if (rKEvt.GetKeyCode().GetCode() == KEY_SPACE)
        {
            if(bisInSdNavigatorWin)
            {
                SvTreeListEntry* pNewEntry = GetCurEntry();
                if (!pNewEntry)
                    return;
                SvTreeListEntry* pParentEntry = GetParent(pNewEntry);
                if (!pParentEntry)
                    return;
                Invalidate();
            }
        }
        else
            SvTreeListBox::KeyInput( rKEvt );
    }

    void SdPageObjsTLB::MouseButtonDown(const MouseEvent& rMEvt)
    {
        mbSelectionHandlerNavigates = rMEvt.GetClicks() == 1;
        comphelper::ScopeGuard aNavigationGuard([this]() { this->mbSelectionHandlerNavigates = false; });
        mbNavigationGrabsFocus = rMEvt.GetClicks() != 1;
        comphelper::ScopeGuard aGrabGuard([this]() { this->mbNavigationGrabsFocus = true; });

        SvTreeListBox::MouseButtonDown(rMEvt);
    }

    /**
     * StartDrag-Request
     */
    void SdPageObjsTLB::StartDrag( sal_Int8, const Point& rPosPixel)
    {
        SdNavigatorWin* pNavWin = nullptr;
        SvTreeListEntry* pEntry = GetEntry(rPosPixel);

        if (mpFrame->HasChildWindow(SID_NAVIGATOR))
        {
            SfxChildWindow* pWnd = mpFrame->GetChildWindow(SID_NAVIGATOR);
            pNavWin = pWnd ? static_cast<SdNavigatorWin*>(pWnd->GetContextWindow(SD_MOD())) : nullptr;
        }

        if (pEntry != nullptr
            && pNavWin !=nullptr
            && pNavWin == mpParent
            && pNavWin->GetNavigatorDragType() != NAVIGATOR_DRAGTYPE_NONE )
        {
            // Mark only the children of the page under the mouse as drop
            // targets.  This prevents moving shapes from one page to another.

            // Select all entries and disable them as drop targets.
            SetSelectionMode(SelectionMode::Multiple);
            SetCursor(static_cast<SvTreeListEntry*>(nullptr));
            SelectAll(true, false);
            EnableSelectionAsDropTarget(false);

            // Enable only the entries as drop targets that are children of the
            // page under the mouse.
            SvTreeListEntry* pParent = GetRootLevelParent(pEntry);
            if (pParent != nullptr)
            {
                SelectAll(false, false);
                Select(pParent);
                //            for (SvTreeListEntry*pChild=FirstChild(pParent); pChild!=NULL; pChild=NextSibling(pChild))
                //                Select(pChild, sal_True);
                EnableSelectionAsDropTarget();//sal_False);
            }

            // Set selection back to the entry under the mouse.
            SelectAll(false, false);
            SetSelectionMode(SelectionMode::Single);
            Select(pEntry);

            // We can delete the Navigator from ExecuteDrag (when switching to
            // another document type), but that would kill the StarView MouseMove
            // Handler which is calling Command().
            // For this reason, Drag&Drop is asynchronous.
            Application::PostUserEvent( LINK( this, SdPageObjsTLB, ExecDragHdl ), nullptr, true );
        }
    }

    /**
     * Begin drag
     */
    void SdPageObjsTLB::DoDrag()
    {
        SfxChildWindow* pWnd = mpFrame->HasChildWindow(SID_NAVIGATOR) ? mpFrame->GetChildWindow(SID_NAVIGATOR) : nullptr;
        mpDropNavWin = pWnd ? static_cast<SdNavigatorWin*>(pWnd->GetContextWindow(SD_MOD())) : nullptr;

        if( mpDropNavWin )
        {
            ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh();
            OUString aURL = INetURLObject( pDocShell->GetMedium()->GetPhysicalName(), INetProtocol::File ).GetMainURL( INetURLObject::DecodeMechanism::NONE );
            NavigatorDragType   eDragType = mpDropNavWin->GetNavigatorDragType();

            aURL += "#" + GetSelectedEntry();

            INetBookmark    aBookmark( aURL, GetSelectedEntry() );
            sal_Int8        nDNDActions = DND_ACTION_COPYMOVE;

            if( eDragType == NAVIGATOR_DRAGTYPE_LINK )
                nDNDActions = DND_ACTION_LINK;  // Either COPY *or* LINK, never both!
            else if (mpDoc->GetSdPageCount(PageKind::Standard) == 1)
            {
                // Can not move away the last slide in a document.
                nDNDActions = DND_ACTION_COPY;
            }

            SvTreeListBox::ReleaseMouse();

            bIsInDrag = true;

            // Get the view.
            ::sd::ViewShell* pViewShell = GetViewShellForDocShell(*pDocShell);
            if (pViewShell == nullptr)
            {
                OSL_ASSERT(pViewShell!=nullptr);
                return;
            }
            sd::View* pView = pViewShell->GetView();
            if (pView == nullptr)
            {
                OSL_ASSERT(pView!=nullptr);
                return;
            }

            // object is destroyed by internal reference mechanism
            SdTransferable* pTransferable =
                    new SdPageObjsTLB::SdPageObjsTransferable(
                                *this, aBookmark, *pDocShell, eDragType);

            SdrObject* pObject = nullptr;
            void* pUserData = GetCurEntry()->GetUserData();
            if (pUserData != nullptr && pUserData != reinterpret_cast<void*>(1))
                pObject = static_cast<SdrObject*>(pUserData);
            if (pObject != nullptr)
            {
                // For shapes without a user supplied name (the automatically
                // created name does not count), a different drag and drop technique
                // is used.
                if (GetObjectName(pObject, false).isEmpty())
                {
                    AddShapeToTransferable(*pTransferable, *pObject);
                    pTransferable->SetView(pView);
                    SD_MOD()->pTransferDrag = pTransferable;
                }

                // Unnamed shapes have to be selected to be recognized by the
                // current drop implementation.  In order to have a consistent
                // behaviour for all shapes, every shape that is to be dragged is
                // selected first.
                SdrPageView* pPageView = pView->GetSdrPageView();
                pView->UnmarkAllObj(pPageView);
                pView->MarkObj(pObject, pPageView);
            }
            else
            {
                pTransferable->SetView(pView);
                SD_MOD()->pTransferDrag = pTransferable;
            }

            pTransferable->StartDrag( this, nDNDActions );
        }
    }

    void SdPageObjsTLB::OnDragFinished()
    {
        if( mpFrame->HasChildWindow( SID_NAVIGATOR ) )
        {
            SfxChildWindow* pWnd = mpFrame->GetChildWindow(SID_NAVIGATOR);
            SdNavigatorWin* pNewNavWin = pWnd ? static_cast<SdNavigatorWin*>(pWnd->GetContextWindow(SD_MOD())) : nullptr;
            if (mpDropNavWin == pNewNavWin)
            {
                MouseEvent aMEvt( mpDropNavWin->GetPointerPosPixel() );
                SvTreeListBox::MouseButtonUp( aMEvt );
            }
        }

        mpDropNavWin = nullptr;
        bIsInDrag = false;
    }

    /**
     * AcceptDrop-Event
     */
    sal_Int8 SdPageObjsTLB::AcceptDrop (const AcceptDropEvent& rEvent)
    {
        sal_Int8 nResult (DND_ACTION_NONE);

        if ( !bIsInDrag && IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) )
        {
            nResult = rEvent.mnAction;
        }
        else
        {
            SvTreeListEntry* pEntry = GetDropTarget(rEvent.maPosPixel);
            if (rEvent.mbLeaving || !CheckDragAndDropMode( this, rEvent.mnAction ))
            {
                ImplShowTargetEmphasis( pTargetEntry, false );
            }
            else if( GetDragDropMode() == DragDropMode::NONE )
            {
                SAL_WARN( "sc.ui", "SdPageObjsTLB::AcceptDrop(): no target" );
            }
            else if (IsDropAllowed(pEntry))
            {
                nResult = DND_ACTION_MOVE;

                // Draw emphasis.
                if (pEntry != pTargetEntry || !(nImpFlags & SvTreeListBoxFlags::TARGEMPH_VIS))
                {
                    ImplShowTargetEmphasis( pTargetEntry, false );
                    pTargetEntry = pEntry;
                    ImplShowTargetEmphasis( pTargetEntry, true );
                }
            }
        }

        // Hide emphasis when there is no valid drop action.
        if (nResult == DND_ACTION_NONE)
            ImplShowTargetEmphasis(pTargetEntry, false);

        return nResult;
    }

    /**
     * ExecuteDrop-Event
     */
    sal_Int8 SdPageObjsTLB::ExecuteDrop( const ExecuteDropEvent& rEvt )
    {
        sal_Int8 nRet = DND_ACTION_NONE;

        try
        {
            if( !bIsInDrag )
            {
                SdNavigatorWin* pNavWin = nullptr;
                sal_uInt16          nId = SID_NAVIGATOR;

                if (mpFrame->HasChildWindow(nId))
                {
                    SfxChildWindow* pWnd = mpFrame->GetChildWindow(nId);
                    pNavWin = pWnd ? static_cast<SdNavigatorWin*>(pWnd->GetContextWindow(SD_MOD())) : nullptr;
                }

                if( pNavWin && ( pNavWin == mpParent ) )
                {
                    TransferableDataHelper  aDataHelper( rEvt.maDropEvent.Transferable );
                    OUString                aFile;

                    if( aDataHelper.GetString( SotClipboardFormatId::SIMPLE_FILE, aFile ) &&
                        static_cast<SdNavigatorWin*>(mpParent.get())->InsertFile( aFile ) )
                    {
                        nRet = rEvt.mnAction;
                    }
                }
            }
        }
        catch (css::uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("sd");
        }

        if (nRet == DND_ACTION_NONE)
            SvTreeListBox::ExecuteDrop(rEvt, this);

        return nRet;
    }

    /**
     * Handler for Dragging
     */
    IMPL_LINK_NOARG(SdPageObjsTLB, ExecDragHdl, void*, void)
    {
        // as link, then it is allowed to asynchronous, without ImpMouseMoveMsg on
        // the stack, delete the Navigator
        DoDrag();
    }

    bool SdPageObjsTLB::PageBelongsToCurrentShow (const SdPage* pPage) const
    {
        // Return <TRUE/> as default when there is no custom show or when none
        // is used.  The page does then belong to the standard show.
        bool bBelongsToShow = true;

        if (mpDoc->getPresentationSettings().mbCustomShow)
        {
            // Get the current custom show.
            SdCustomShow* pCustomShow = nullptr;
            SdCustomShowList* pShowList = const_cast<SdDrawDocument*>(mpDoc)->GetCustomShowList();
            if (pShowList != nullptr)
            {
                sal_uLong nCurrentShowIndex = pShowList->GetCurPos();
                pCustomShow = (*pShowList)[nCurrentShowIndex];
            }

            // Check whether the given page is part of that custom show.
            if (pCustomShow != nullptr)
            {
                bBelongsToShow = false;
                size_t nPageCount = pCustomShow->PagesVector().size();
                for (size_t i=0; i<nPageCount && !bBelongsToShow; i++)
                    if (pPage == pCustomShow->PagesVector()[i])
                        bBelongsToShow = true;
            }
        }

        return bBelongsToShow;
    }

    TriState SdPageObjsTLB::NotifyMoving(
        SvTreeListEntry* pTarget,
        SvTreeListEntry* pEntry,
        SvTreeListEntry*& rpNewParent,
        sal_uLong& rNewChildPos)
    {
        SvTreeListEntry* pDestination = pTarget;
        while (GetParent(pDestination) != nullptr && GetParent(GetParent(pDestination)) != nullptr)
            pDestination = GetParent(pDestination);

        SdrObject* pTargetObject = static_cast<SdrObject*>(pDestination->GetUserData());
        SdrObject* pSourceObject = static_cast<SdrObject*>(pEntry->GetUserData());
        if (pSourceObject == reinterpret_cast<SdrObject*>(1))
            pSourceObject = nullptr;

        if (pTargetObject != nullptr && pSourceObject != nullptr)
        {
            SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject();
            if (pObjectList != nullptr)
            {
                sal_uInt32 nNewPosition;
                if (pTargetObject == reinterpret_cast<SdrObject*>(1))
                    nNewPosition = 0;
                else
                    nNewPosition = pTargetObject->GetNavigationPosition() + 1;
                pObjectList->SetObjectNavigationPosition(*pSourceObject, nNewPosition);
            }

            // Update the tree list.
            if (GetParent(pDestination) == nullptr)
            {
                rpNewParent = pDestination;
                rNewChildPos = 0;
            }
            else
            {
                rpNewParent = GetParent(pDestination);
                rNewChildPos = SvTreeList::GetRelPos(pDestination) + 1;
                rNewChildPos += nCurEntrySelPos;
                nCurEntrySelPos++;
            }
            return TRISTATE_TRUE;
        }
        else
            return TRISTATE_FALSE;
    }

    SvTreeListEntry* SdPageObjsTLB::GetDropTarget (const Point& rLocation)
    {
        SvTreeListEntry* pEntry = SvTreeListBox::GetDropTarget(rLocation);
        if (pEntry == nullptr)
            return nullptr;

        if (GetParent(pEntry) == nullptr)
        {
            // Use page entry as insertion position.
        }
        else
        {
            // Go to second hierarchy level, i.e. top level shapes,
            // i.e. children of pages.
            while (GetParent(pEntry) != nullptr && GetParent(GetParent(pEntry)) != nullptr)
                pEntry = GetParent(pEntry);

            // Advance to next sibling.
            SvTreeListEntry* pNext;
            sal_uInt16 nDepth (0);
            do
            {
                pNext = NextVisible(pEntry, &nDepth);
                if (pNext != nullptr && nDepth > 0 && nDepth!=0xffff)
                    pEntry = pNext;
                else
                    break;
            }
            while (pEntry != nullptr);
        }

        return pEntry;
    }

    bool SdPageObjsTLB::IsDropAllowed (SvTreeListEntry const * pEntry)
    {
        if (pEntry == nullptr)
            return false;

        if ( ! IsDropFormatSupported(SdPageObjsTransferable::GetListBoxDropFormatId()))
            return false;

        if (pEntry->GetFlags() & SvTLEntryFlags::DISABLE_DROP)
            return false;

        return true;
    }

    void SdPageObjsTLB::AddShapeToTransferable (
        SdTransferable& rTransferable,
        SdrObject& rObject) const
    {
        std::unique_ptr<TransferableObjectDescriptor> pObjectDescriptor(new TransferableObjectDescriptor);
        bool bIsDescriptorFillingPending (true);

        const SdrOle2Obj* pOleObject = dynamic_cast<const SdrOle2Obj*>(&rObject);
        if (pOleObject != nullptr && pOleObject->GetObjRef().is())
        {
            // If object has no persistence it must be copied as part of the document
            try
            {
                uno::Reference< embed::XEmbedPersist > xPersObj (pOleObject->GetObjRef(), uno::UNO_QUERY );
                if (xPersObj.is() && xPersObj->hasEntry())
                {
                    SvEmbedTransferHelper::FillTransferableObjectDescriptor(
                        *pObjectDescriptor,
                        pOleObject->GetObjRef(),
                        pOleObject->GetGraphic(),
                        pOleObject->GetAspect());
                    bIsDescriptorFillingPending = false;
                }
            }
            catch( uno::Exception& )
            {
            }
        }

        ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh();
        if (bIsDescriptorFillingPending && pDocShell!=nullptr)
        {
            pDocShell->FillTransferableObjectDescriptor(*pObjectDescriptor);
        }

        Point aDragPos (rObject.GetCurrentBoundRect().Center());
        //Point aDragPos (0,0);
        pObjectDescriptor->maDragStartPos = aDragPos;
        //  aObjectDescriptor.maSize = GetAllMarkedRect().GetSize();
        if (pDocShell != nullptr)
            pObjectDescriptor->maDisplayName = pDocShell->GetMedium()->GetURLObject().GetURLNoPass();
        else
            pObjectDescriptor->maDisplayName.clear();

        rTransferable.SetStartPos(aDragPos);
        rTransferable.SetObjectDescriptor( std::move(pObjectDescriptor) );
    }

    ::sd::ViewShell* SdPageObjsTLB::GetViewShellForDocShell (::sd::DrawDocShell& rDocShell)
    {
        {
            ::sd::ViewShell* pViewShell = rDocShell.GetViewShell();
            if (pViewShell != nullptr)
                return pViewShell;
        }

        try
        {
            // Get a component enumeration from the desktop and search it for documents.
            uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext());

            uno::Reference<frame::XDesktop2> xDesktop = frame::Desktop::create(xContext);

            uno::Reference<frame::XFramesSupplier> xFrameSupplier (xDesktop, uno::UNO_QUERY);
            if ( ! xFrameSupplier.is())
                return nullptr;

            uno::Reference<container::XIndexAccess> xFrameAccess (xFrameSupplier->getFrames(), uno::UNO_QUERY);
            if ( ! xFrameAccess.is())
                return nullptr;

            for (sal_Int32 nIndex=0,nCount=xFrameAccess->getCount(); nIndex<nCount; ++nIndex)
            {
                uno::Reference<frame::XFrame> xFrame;
                if ( ! (xFrameAccess->getByIndex(nIndex) >>= xFrame))
                    continue;

                ::sd::DrawController* pController = dynamic_cast<sd::DrawController*>(xFrame->getController().get());
                if (pController == nullptr)
                    continue;
                ::sd::ViewShellBase* pBase = pController->GetViewShellBase();
                if (pBase == nullptr)
                    continue;
                if (pBase->GetDocShell() != &rDocShell)
                    continue;

                const std::shared_ptr<sd::ViewShell> pViewShell (pBase->GetMainViewShell());
                if (pViewShell)
                    return pViewShell.get();
            }
        }
        catch (uno::Exception &)
        {
            // When there is an exception then simply use the default value of
            // bIsEnabled and disable the controls.
        }
        return nullptr;
    }

    //===== IconProvider ==========================================================

    SdPageObjsTLB::IconProvider::IconProvider()
        : maImgPage(BitmapEx(BMP_PAGE)),
          maImgPageExcl(BitmapEx(BMP_PAGE_EXCLUDED)),
          maImgPageObjsExcl(BitmapEx(BMP_PAGEOBJS_EXCLUDED)),
          maImgPageObjs(BitmapEx(BMP_PAGEOBJS)),
          maImgObjects(BitmapEx(BMP_OBJECTS)),
          maImgGroup(BitmapEx(BMP_GROUP))
    {
    }

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

↑ V654 The condition 'pEntry != nullptr' of loop is always true.