    /* -*- 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 <svx/svditer.hxx>
    #include <svx/svdobj.hxx>
    #include <svx/svdpage.hxx>
    #include <svx/svdpagv.hxx>
    #include <svx/svdview.hxx>
    #include <svx/svdxcgv.hxx>
    #include <sfx2/linkmgr.hxx>
    #include <sfx2/docfile.hxx>
    #include <sfx2/viewfrm.hxx>
    #include <vcl/help.hxx>
    #include <vcl/svapp.hxx>
    #include <tools/urlobj.hxx>
    #include <svl/urlbmk.hxx>
    #include <svtools/svlbitm.hxx>
    #include <svtools/treelistentry.hxx>
    #include <stdlib.h>
    #include <sal/log.hxx>

    #include <content.hxx>
    #include <navipi.hxx>
    #include <global.hxx>
    #include <docsh.hxx>
    #include <scmod.hxx>
    #include <rangenam.hxx>
    #include <dbdata.hxx>
    #include <tablink.hxx>
    #include <drwlayer.hxx>
    #include <transobj.hxx>
    #include <drwtrans.hxx>
    #include <lnktrans.hxx>
    #include <formulacell.hxx>
    #include <dociter.hxx>
    #include <strings.hrc>
    #include <scresid.hxx>
    #include <bitmaps.hlst>
    #include <arealink.hxx>
    #include <navicfg.hxx>
    #include <navsett.hxx>
    #include <postit.hxx>
    #include <tabvwsh.hxx>
    #include <drawview.hxx>
    #include <clipparam.hxx>
    #include <markdata.hxx>
    #include <o3tl/make_unique.hxx>
    using namespace com::sun::star;

    //  order of the categories in navigator -------------------------------------

    static const ScContentId pTypeList[int(ScContentId::LAST) + 1] =
    {
        ScContentId::ROOT,            // ROOT (0) has to be at the front
        ScContentId::TABLE,
        ScContentId::RANGENAME,
        ScContentId::DBAREA,
        ScContentId::AREALINK,
        ScContentId::GRAPHIC,
        ScContentId::OLEOBJECT,
        ScContentId::NOTE,
        ScContentId::DRAWING
    };

    static const OUStringLiteral aContentBmps[]=
    {
        RID_BMP_CONTENT_TABLE,
        RID_BMP_CONTENT_RANGENAME,
        RID_BMP_CONTENT_DBAREA,
        RID_BMP_CONTENT_GRAPHIC,
        RID_BMP_CONTENT_OLEOBJECT,
        RID_BMP_CONTENT_NOTE,
        RID_BMP_CONTENT_AREALINK,
        RID_BMP_CONTENT_DRAWING
    };

    bool ScContentTree::bIsInDrag = false;

    ScDocShell* ScContentTree::GetManualOrCurrent()
    {
        ScDocShell* pSh = nullptr;
        if ( !aManualDoc.isEmpty() )
        {
            SfxObjectShell* pObjSh = SfxObjectShell::GetFirst( checkSfxObjectShell<ScDocShell> );
            while ( pObjSh && !pSh )
            {
                if ( pObjSh->GetTitle() == aManualDoc )
                    pSh = dynamic_cast<ScDocShell*>( pObjSh  );
                pObjSh = SfxObjectShell::GetNext( *pObjSh, checkSfxObjectShell<ScDocShell> );
            }
        }
        else
        {
            //  only current when manual isn't set
            //  (so it's detected when the documents don't exists any longer)

            SfxViewShell* pViewSh = SfxViewShell::Current();
            if ( pViewSh )
            {
                SfxObjectShell* pObjSh = pViewSh->GetViewFrame()->GetObjectShell();
                pSh = dynamic_cast<ScDocShell*>( pObjSh  );
            }
        }

        return pSh;
    }

    //          ScContentTree

    ScContentTree::ScContentTree(vcl::Window* pParent, ScNavigatorDlg* pNavigatorDlg)
        : SvTreeListBox(pParent, WB_BORDER | WB_TABSTOP)
        , pParentWindow(pNavigatorDlg)
        , nRootType(ScContentId::ROOT)
        , bHiddenDoc(false)
        , pHiddenDocument(nullptr)
        , bisInNavigatoeDlg(false)
    {
        SetQuickSearch(true);
        for (sal_uInt16 i = 0; i <= int(ScContentId::LAST); ++i)
            pPosList[pTypeList[i]] = i;         // inverse for searching

        pRootNodes[ScContentId::ROOT] = nullptr;
        for (sal_uInt16 i = 1; i < int(ScContentId::LAST); ++i)
            InitRoot(static_cast<ScContentId>(i));

        SetNodeDefaultImages();

        SetDoubleClickHdl( LINK( this, ScContentTree, ContentDoubleClickHdl ) );
    }

    Size ScContentTree::GetOptimalSize() const
    {
        return LogicToPixel(Size(110, 100), MapMode(MapUnit::MapAppFont));
    }

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

    void ScContentTree::dispose()
    {
        pParentWindow.clear();
        SvTreeListBox::dispose();
    }

    // helper function for  GetEntryAltText and GetEntryLongDescription
    OUString ScContentTree::getAltLongDescText( SvTreeListEntry* pEntry, bool isAltText) const
    {

        ScContentId nType;
        sal_uLong nChild;
        GetEntryIndexes( nType, nChild, pEntry );
        switch( nType )
        {
        case ScContentId::OLEOBJECT:
        case ScContentId::GRAPHIC:
        case ScContentId::DRAWING:
            {
                SdrObject* pFound = nullptr;
                ScDocument* pDoc =  const_cast< ScContentTree* >(this)->GetSourceDocument();
                SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
                ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
                SfxObjectShell* pShell = pDoc->GetDocumentShell();
                if (pDrawLayer && pShell)
                {
                    sal_uInt16 nTabCount = pDoc->GetTableCount();
                    for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++)
                    {
                        SdrPage* pPage = pDrawLayer->GetPage(nTab);
                        DBG_ASSERT(pPage,"Page ?");
                        if (pPage)
                        {
                            SdrObjListIter aIter( pPage, eIter );
                            SdrObject* pObject = aIter.Next();
                            while (pObject)
                            {
                                if( ScDrawLayer::GetVisibleName( pObject ) == GetEntryText( pEntry ) )
                                {
                                    pFound = pObject;
                                    break;
                                }
                                pObject = aIter.Next();
                            }
                        }
                    }
                }
                if( pFound )
                {
                    if( isAltText )
                        return pFound->GetTitle();
                    else
                        return pFound->GetDescription();
                }
            }
            break;
            default: break;
        }
        return OUString();
    }

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

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

    static const char* SCSTR_CONTENT_ARY[] =
    {
        SCSTR_CONTENT_ROOT,
        SCSTR_CONTENT_TABLE,
        SCSTR_CONTENT_RANGENAME,
        SCSTR_CONTENT_DBAREA,
        SCSTR_CONTENT_GRAPHIC,
        SCSTR_CONTENT_OLEOBJECT,
        SCSTR_CONTENT_NOTE,
        SCSTR_CONTENT_AREALINK,
        SCSTR_CONTENT_DRAWING
    };

    void ScContentTree::InitRoot( ScContentId nType )
    {
        if ( nType == ScContentId::ROOT )
            return;

        if ( nRootType != ScContentId::ROOT && nRootType != nType )              // hidden ?
        {
            pRootNodes[nType] = nullptr;
            return;
        }

        BitmapEx aBitmap(aContentBmps[static_cast<int>(nType) - 1]);
        Image aImage(aBitmap);
        OUString aName(ScResId(SCSTR_CONTENT_ARY[static_cast<int>(nType)]));
        // back to the correct position:
        sal_uInt16 nPos = nRootType != ScContentId::ROOT ? 0 : pPosList[nType]-1;
        SvTreeListEntry* pNew = InsertEntry( aName, aImage, aImage, nullptr, false, nPos );

        pRootNodes[nType] = pNew;
    }

    void ScContentTree::ClearAll()
    {
        //There are one method in Control::SetUpdateMode(), and one override method SvTreeListBox::SetUpdateMode(). Here although
        //SvTreeListBox::SetUpdateMode() is called in refresh method, it only call SvTreeListBox::SetUpdateMode(), not Control::SetUpdateMode().
        //In SvTreeList::Clear(), Broadcast( LISTACTION_CLEARED ) will be called and finally, it will be trapped into the event yield() loop. And
        //the InitRoot() method won't be called. Then if a user click or press key to update the navigator tree, crash happens.
        //So the solution is to disable the UpdateMode of Control, then call Clear(), then recover the update mode
        bool bOldUpdate = Control::IsUpdateMode();
        Control::SetUpdateMode(false);
        Clear();
        Control::SetUpdateMode(bOldUpdate);
        for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
            InitRoot(static_cast<ScContentId>(i));
    }

    void ScContentTree::ClearType(ScContentId nType)
    {
        if (nType == ScContentId::ROOT)
            ClearAll();
        else
        {
            SvTreeListEntry* pParent = pRootNodes[nType];
            if ( !pParent || GetChildCount(pParent) )       // not if no children existing
            {
                if (pParent)
                    GetModel()->Remove( pParent );          // with all children
                InitRoot( nType );                          // if needed insert anew
            }
        }
    }

    void ScContentTree::InsertContent( ScContentId nType, const OUString& rValue )
    {
        SvTreeListEntry* pParent = pRootNodes[nType];
        if (pParent)
            InsertEntry( rValue, pParent );
        else
        {
            OSL_FAIL("InsertContent without parent");
        }
    }

    void ScContentTree::GetEntryIndexes( ScContentId& rnRootIndex, sal_uLong& rnChildIndex, SvTreeListEntry* pEntry ) const
    {
        rnRootIndex = ScContentId::ROOT;
        rnChildIndex = SC_CONTENT_NOCHILD;

        if( !pEntry )
            return;

        SvTreeListEntry* pParent = GetParent( pEntry );
        bool bFound = false;
        for( int i = 1; !bFound && (i <= int(ScContentId::LAST)); ++i )
        {
            ScContentId nRoot = static_cast<ScContentId>(i);
            if( pEntry == pRootNodes[ nRoot ] )
            {
                rnRootIndex = nRoot;
                rnChildIndex = ~0UL;
                bFound = true;
            }
            else if( pParent && (pParent == pRootNodes[ nRoot ]) )
            {
                rnRootIndex = nRoot;

                // search the entry in all child entries of the parent
                sal_uLong nEntry = 0;
                SvTreeListEntry* pIterEntry = FirstChild( pParent );
                while( !bFound && pIterEntry )
                {
                    if ( pEntry == pIterEntry )
                    {
                        rnChildIndex = nEntry;
                        bFound = true;  // exit the while loop
                    }
                    pIterEntry = pIterEntry->NextSibling();
                    ++nEntry;
                }

                bFound = true;  // exit the for loop
            }
        }
    }

    sal_uLong ScContentTree::GetChildIndex( SvTreeListEntry* pEntry ) const
    {
        ScContentId nRoot;
        sal_uLong nChild;
        GetEntryIndexes( nRoot, nChild, pEntry );
        return nChild;
    }

    static OUString lcl_GetDBAreaRange( const ScDocument* pDoc, const OUString& rDBName )
    {
        OUString aRet;
        if (pDoc)
        {
            ScDBCollection* pDbNames = pDoc->GetDBCollection();
            const ScDBData* pData = pDbNames->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rDBName));
            if (pData)
            {
                ScRange aRange;
                pData->GetArea(aRange);
                aRet = aRange.Format(ScRefFlags::RANGE_ABS_3D, pDoc);
            }
        }
        return aRet;
    }

    IMPL_LINK_NOARG(ScContentTree, ContentDoubleClickHdl, SvTreeListBox*, bool)
    {
        ScContentId nType;
        sal_uLong nChild;
        SvTreeListEntry* pEntry = GetCurEntry();
        GetEntryIndexes( nType, nChild, pEntry );

        if( pEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD) )
        {
            if ( bHiddenDoc )
                return false;               //! later...

            OUString aText( GetEntryText( pEntry ) );

            if ( !aManualDoc.isEmpty() )
                pParentWindow->SetCurrentDoc( aManualDoc );

            switch( nType )
            {
                case ScContentId::TABLE:
                    pParentWindow->SetCurrentTableStr( aText );
                break;

                case ScContentId::RANGENAME:
                    pParentWindow->SetCurrentCellStr( aText );
                break;

                case ScContentId::DBAREA:
                {
                    //  If the same names of area and DB exists, then
                    //  SID_CURRENTCELL takes the area name.
                    //  Therefore for DB areas access them directly via address.

                    OUString aRangeStr = lcl_GetDBAreaRange( GetSourceDocument(), aText );
                    if (!aRangeStr.isEmpty())
                        pParentWindow->SetCurrentCellStr( aRangeStr );
                }
                break;

                case ScContentId::OLEOBJECT:
                case ScContentId::GRAPHIC:
                case ScContentId::DRAWING:
                    pParentWindow->SetCurrentObject( aText );
                break;

                case ScContentId::NOTE:
                {
                    ScAddress aPos = GetNotePos( nChild );
                    pParentWindow->SetCurrentTable( aPos.Tab() );
                    pParentWindow->SetCurrentCell( aPos.Col(), aPos.Row() );
                }
                break;

                case ScContentId::AREALINK:
                {
                    const ScAreaLink* pLink = GetLink( nChild );
                    if( pLink )
                    {
                        ScRange aRange = pLink->GetDestArea();
                        ScDocument* pSrcDoc = GetSourceDocument();
                        OUString aRangeStr(aRange.Format(ScRefFlags::RANGE_ABS_3D, pSrcDoc, pSrcDoc->GetAddressConvention()));
                        pParentWindow->SetCurrentCellStr( aRangeStr );
                    }
                }
                break;
                default: break;
            }

            ScNavigatorDlg::ReleaseFocus();     // set focus into document
        }

        return true;
    }

    void ScContentTree::MouseButtonDown( const MouseEvent& rMEvt )
    {
        SvTreeListBox::MouseButtonDown( rMEvt );
        StoreNavigatorSettings();
    }

    void ScContentTree::KeyInput( const KeyEvent& rKEvt )
    {
        bool bUsed = false;

        const vcl::KeyCode aCode = rKEvt.GetKeyCode();
        if (aCode.GetCode() == KEY_RETURN)
        {
            switch (aCode.GetModifier())
            {
                case KEY_MOD1:
                    ToggleRoot();       // toggle root mode (as in Writer)
                    bUsed = true;
                    break;
                case 0:
                {
                    SvTreeListEntry* pEntry = GetCurEntry();
                    if( pEntry )
                    {
                        ScContentId nType;
                        sal_uLong nChild;
                        GetEntryIndexes( nType, nChild, pEntry );

                        if( (nType != ScContentId::ROOT) && (nChild == SC_CONTENT_NOCHILD) )
                        {
                            if ( IsExpanded( pEntry ) )
                                Collapse( pEntry );
                            else
                                Expand( pEntry );
                        }
                        else
                            ContentDoubleClickHdl(nullptr);      // select content as if double clicked
                    }

                    bUsed = true;
                }
                break;
            }
        }
        //Make KEY_SPACE has same function as DoubleClick
        if ( bisInNavigatoeDlg )
        {
            if(aCode.GetCode() == KEY_SPACE )
            {
                bUsed = true;
                ScContentId nType;
                sal_uLong nChild;
                SvTreeListEntry* pEntry = GetCurEntry();
                GetEntryIndexes( nType, nChild, pEntry );
                if( pEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD) )
                {
                    if ( bHiddenDoc )
                        return ;                //! later...
                    OUString aText( GetEntryText( pEntry ) );
                    sKeyString = aText;
                    if (!aManualDoc.isEmpty())
                        pParentWindow->SetCurrentDoc( aManualDoc );
                    switch( nType )
                    {
                        case ScContentId::OLEOBJECT:
                        case ScContentId::GRAPHIC:
                        case ScContentId::DRAWING:
                        {
                            vcl::Window* pWindow=reinterpret_cast<vcl::Window*>(GetParent(pEntry));
                            ScNavigatorDlg* pScNavigatorDlg = static_cast<ScNavigatorDlg*>(pWindow);
                            ScTabViewShell* pScTabViewShell = nullptr;
                            ScDrawView* pScDrawView = nullptr;
                            if (pScNavigatorDlg!=nullptr)
                                  pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
                            if(pScTabViewShell !=nullptr)
                                  pScDrawView =pScTabViewShell->GetViewData().GetScDrawView();
                            if(pScDrawView!=nullptr)
                             {
                                pScDrawView->SelectCurrentViewObject(aText );
                                bool bHasMakredObject = false;
                                SvTreeListEntry* pParent = pRootNodes[nType];
                                SvTreeListEntry* pBeginEntry = nullptr;
                                if( pParent )
                                    pBeginEntry = FirstChild(pParent);
                                while( pBeginEntry )
                                {
                                    OUString aTempText( GetEntryText( pBeginEntry ) );
                                     if( pScDrawView->GetObjectIsMarked( pScDrawView->GetObjectByName( aTempText ) ) )
                                     {
                                        bHasMakredObject = true;
                                        break;
                                      }
                                    pBeginEntry =  Next( pBeginEntry );
                                }
                                if(  !bHasMakredObject && pScTabViewShell)
                                    pScTabViewShell->SetDrawShell(false);
                                ObjectFresh( nType,pEntry );
                            }
                        }
                        break;
                        default: break;
                     }
                }
               }
           }

        if( !bUsed )
        {
            if(aCode.GetCode() == KEY_F5 )
            {
                StoreNavigatorSettings();
                SvTreeListBox::KeyInput(rKEvt);
            }
            else
            {
                SvTreeListBox::KeyInput(rKEvt);
                StoreNavigatorSettings();
            }
        }
    }

    sal_Int8 ScContentTree::AcceptDrop( const AcceptDropEvent& /* rEvt */ )
    {
        return DND_ACTION_NONE;
    }

    sal_Int8 ScContentTree::ExecuteDrop( const ExecuteDropEvent& /* rEvt */ )
    {
        return DND_ACTION_NONE;
    }

    void ScContentTree::StartDrag( sal_Int8 /* nAction */, const Point& /* rPosPixel */ )
    {
        DoDrag();
    }

    void ScContentTree::DragFinished( sal_Int8 /* nAction */ )
    {
    }

    void ScContentTree::Command( const CommandEvent& rCEvt )
    {
        bool bDone = false;

        switch ( rCEvt.GetCommand() )
        {
            case CommandEventId::StartDrag:
                //  From within ExecuteDrag you can delete the navigator
                //  (during switch over to another document type), but this would
                //  kill the StarView MouseMove handler, who calls Command().
                //  Therefore async Drag&Drop:

                Application::PostUserEvent( LINK( this, ScContentTree, ExecDragHdl ), nullptr, true );

                bDone = true;
                break;

            case CommandEventId::ContextMenu:
                {
                    //  drag-and-drop mode

                    ScopedVclPtrInstance<PopupMenu> aPop;
                    VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "modules/scalc/ui/dropmenu.ui", "");
                    VclPtr<PopupMenu> aDropMenu(aBuilder.get_menu("menu"));

                    switch (pParentWindow->GetDropMode())
                    {
                        case 0:
                            aDropMenu->CheckItem("hyperlink");
                            break;
                        case 1:
                            aDropMenu->CheckItem("link");
                            break;
                        case 2:
                            aDropMenu->CheckItem("copy");
                            break;
                    }

                    aPop->InsertItem( 1, pParentWindow->GetStrDragMode() );
                    aPop->SetPopupMenu( 1, aDropMenu.get() );

                    //  displayed document

                    VclPtrInstance<PopupMenu> aDocMenu;
                    aDocMenu->SetMenuFlags( aDocMenu->GetMenuFlags() | MenuFlags::NoAutoMnemonics );
                    sal_uInt16 i=0;
                    sal_uInt16 nPos=0;
                    //  loaded documents
                    ScDocShell* pCurrentSh = dynamic_cast<ScDocShell*>( SfxObjectShell::Current()  );
                    SfxObjectShell* pSh = SfxObjectShell::GetFirst();
                    while ( pSh )
                    {
                        if ( dynamic_cast<const ScDocShell*>( pSh) !=  nullptr )
                        {
                            OUString aName = pSh->GetTitle();
                            OUString aEntry = aName;
                            if ( pSh == pCurrentSh )
                                aEntry += pParentWindow->aStrActive;
                            else
                                aEntry += pParentWindow->aStrNotActive;
                            ++i;
                            aDocMenu->InsertItem(i, aEntry, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, OString("document") + OString::number(i));
                            if ( !bHiddenDoc && aName == aManualDoc )
                                nPos = i;
                        }
                        pSh = SfxObjectShell::GetNext( *pSh );
                    }
                    //  "active window"
                    ++i;
                    aDocMenu->InsertItem(i, pParentWindow->aStrActiveWin, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, OString("document") + OString::number(i));
                    if (!bHiddenDoc && aManualDoc.isEmpty())
                        nPos = i;
                    //  hidden document
                    if ( !aHiddenTitle.isEmpty() )
                    {
                        OUString aEntry = aHiddenTitle;
                        aEntry += pParentWindow->aStrHidden;
                        ++i;
                        aDocMenu->InsertItem(i, aEntry, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, OString("document") + OString::number(i));
                        if (bHiddenDoc)
                            nPos = i;
                    }
                    aDocMenu->CheckItem( nPos );
                    aPop->InsertItem( 2, pParentWindow->GetStrDisplay() );
                    aPop->SetPopupMenu( 2, aDocMenu.get() );

                    sal_uInt16 nSelected = aPop->Execute(this, rCEvt.GetMousePosPixel());
                    OString sIdent = aPop->GetItemIdentFromSubMenu(nSelected);

                    if (sIdent.startsWith("document"))
                    {
                        OUString aName = aDocMenu->GetItemText(aDocMenu->GetItemId(sIdent));
                        SelectDoc(aName);
                    }
                    else if (sIdent == "hyperlink")
                        pParentWindow->SetDropMode(0);
                    else if (sIdent == "link")
                        pParentWindow->SetDropMode(1);
                    else if (sIdent == "copy")
                        pParentWindow->SetDropMode(2);
                }
                break;
                default: break;
        }

        if (!bDone)
            SvTreeListBox::Command(rCEvt);
    }

    void ScContentTree::RequestHelp( const HelpEvent& rHEvt )
    {
        bool bDone = false;
        if( rHEvt.GetMode() & HelpEventMode::QUICK )
        {
            Point aPos( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ));
            SvTreeListEntry* pEntry = GetEntry( aPos );
            if ( pEntry )
            {
                bool bRet = false;
                OUString aHelpText;
                SvTreeListEntry* pParent = GetParent(pEntry);
                if ( !pParent )                                 // Top-Level ?
                {
                    aHelpText = OUString::number( GetChildCount(pEntry) ) +
                                " " + GetEntryText(pEntry);
                    bRet = true;
                }
                else if ( pParent == pRootNodes[ScContentId::NOTE] )
                {
                    aHelpText = GetEntryText(pEntry);           // notes as help text
                    bRet = true;
                }
                else if ( pParent == pRootNodes[ScContentId::AREALINK] )
                {
                    sal_uLong nIndex = GetChildIndex(pEntry);
                    if( nIndex != SC_CONTENT_NOCHILD )
                    {
                        const ScAreaLink* pLink = GetLink(nIndex);
                        if (pLink)
                        {
                            aHelpText = pLink->GetFile();           // source file as help text
                            bRet = true;
                        }
                    }
                }

                if (bRet)
                {
                    SvLBoxTab* pTab;
                    SvLBoxString* pItem = static_cast<SvLBoxString*>(GetItem( pEntry, aPos.X(), &pTab ));
                    if( pItem )
                    {
                        aPos = GetEntryPosition( pEntry );
                        aPos.setX( GetTabPos( pEntry, pTab ) );
                        aPos = OutputToScreenPixel(aPos);
                        Size aSize( pItem->GetSize( this, pEntry ) );

                        tools::Rectangle aItemRect( aPos, aSize );
                        Help::ShowQuickHelp( this, aItemRect, aHelpText );
                        bDone = true;
                    }
                }
            }
        }
        if (!bDone)
            Window::RequestHelp( rHEvt );
    }

    ScDocument* ScContentTree::GetSourceDocument()
    {
        if (bHiddenDoc)
            return pHiddenDocument;
        else
        {
            ScDocShell* pSh = GetManualOrCurrent();
            if (pSh)
                return &pSh->GetDocument();

        }
        return nullptr;
    }

    //Move along and draw "*" sign .
    void ScContentTree::ObjectFresh( ScContentId nType, const SvTreeListEntry* pEntry )
    {
        if ( bHiddenDoc && !pHiddenDocument )
            return;     // other document displayed
          if(nType ==ScContentId::GRAPHIC||nType ==ScContentId::OLEOBJECT||nType ==ScContentId::DRAWING)
            {
            SetUpdateMode(false);
            ClearType( nType );
            GetDrawNames( nType/*, nId*/ );
            if( !pEntry )
                ApplyNavigatorSettings();
            SetUpdateMode(true);
            if( pEntry )
            {
                SvTreeListEntry* pParent = pRootNodes[nType];
                SvTreeListEntry* pBeginEntry = nullptr;
                SvTreeListEntry* pOldEntry = nullptr;
                if( pParent )
                    pBeginEntry = FirstChild(pParent);
                while( pBeginEntry )
                {
                    OUString aTempText( GetEntryText( pBeginEntry ) );
                    if( aTempText ==  sKeyString )
                    {
                        pOldEntry = pBeginEntry;
                        break;
                    }
                    pBeginEntry =  Next( pBeginEntry );
                }
                if( pOldEntry )
                {
                    Expand(pParent);
                    Select( pOldEntry );
                }
            }
            }
    }

    void ScContentTree::Refresh( ScContentId nType )
    {
        if ( bHiddenDoc && !pHiddenDocument )
            return;                                 // other document displayed

        //  if nothing has changed the cancel right away (against flicker)

        if ( nType == ScContentId::NOTE )
            if (!NoteStringsChanged())
                return;
        if ( nType == ScContentId::GRAPHIC )
            if (!DrawNamesChanged(ScContentId::GRAPHIC))
                return;
        if ( nType == ScContentId::OLEOBJECT )
            if (!DrawNamesChanged(ScContentId::OLEOBJECT))
                return;
        if ( nType == ScContentId::DRAWING )
            if (!DrawNamesChanged(ScContentId::DRAWING))
                return;

        SetUpdateMode(false);

        ClearType( nType );

        if ( nType == ScContentId::ROOT || nType == ScContentId::TABLE )
            GetTableNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::RANGENAME )
            GetAreaNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::DBAREA )
            GetDbNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::GRAPHIC )
            GetGraphicNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::OLEOBJECT )
            GetOleNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::DRAWING )
            GetDrawingNames();
        if ( nType == ScContentId::ROOT || nType == ScContentId::NOTE )
            GetNoteStrings();
        if ( nType == ScContentId::ROOT || nType == ScContentId::AREALINK )
            GetLinkNames();

        ApplyNavigatorSettings();
        SetUpdateMode(true);
    }

    void ScContentTree::GetTableNames()
    {
        if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::TABLE )       // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        OUString aName;
        SCTAB nCount = pDoc->GetTableCount();
        for ( SCTAB i=0; i<nCount; i++ )
        {
            pDoc->GetName( i, aName );
            InsertContent( ScContentId::TABLE, aName );
        }
    }

    namespace {

    OUString createLocalRangeName(const OUString& rName, const OUString& rTableName)
    {
        OUStringBuffer aString (rName);
        aString.append(" (");
        aString.append(rTableName);
        aString.append(")");
        return aString.makeStringAndClear();
    }
    }

    void ScContentTree::GetAreaNames()
    {
        if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::RANGENAME )       // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        ScRange aDummy;
        std::set<OUString> aSet;
        ScRangeName* pRangeNames = pDoc->GetRangeName();
        if (!pRangeNames->empty())
        {
            ScRangeName::const_iterator itrBeg = pRangeNames->begin(), itrEnd = pRangeNames->end();
            for (ScRangeName::const_iterator itr = itrBeg; itr != itrEnd; ++itr)
            {
                if (itr->second->IsValidReference(aDummy))
                    aSet.insert(itr->second->GetName());
            }
        }
        for (SCTAB i = 0; i < pDoc->GetTableCount(); ++i)
        {
            ScRangeName* pLocalRangeName = pDoc->GetRangeName(i);
            if (pLocalRangeName && !pLocalRangeName->empty())
            {
                OUString aTableName;
                pDoc->GetName(i, aTableName);
                for (ScRangeName::const_iterator itr = pLocalRangeName->begin(); itr != pLocalRangeName->end(); ++itr)
                {
                    if (itr->second->IsValidReference(aDummy))
                        aSet.insert(createLocalRangeName(itr->second->GetName(), aTableName));
                }
            }
        }

        if (!aSet.empty())
        {
            for (std::set<OUString>::iterator itr = aSet.begin();
                    itr != aSet.end(); ++itr)
            {
                InsertContent(ScContentId::RANGENAME, *itr);
            }
        }
    }

    void ScContentTree::GetDbNames()
    {
        if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::DBAREA )      // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        ScDBCollection* pDbNames = pDoc->GetDBCollection();
        const ScDBCollection::NamedDBs& rDBs = pDbNames->getNamedDBs();
        ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin(), itrEnd = rDBs.end();
        for (; itr != itrEnd; ++itr)
        {
            const OUString& aStrName = (*itr)->GetName();
            InsertContent(ScContentId::DBAREA, aStrName);
        }
    }

    bool ScContentTree::IsPartOfType( ScContentId nContentType, sal_uInt16 nObjIdentifier )
    {
        bool bRet = false;
        switch ( nContentType )
        {
            case ScContentId::GRAPHIC:
                bRet = ( nObjIdentifier == OBJ_GRAF );
                break;
            case ScContentId::OLEOBJECT:
                bRet = ( nObjIdentifier == OBJ_OLE2 );
                break;
            case ScContentId::DRAWING:
                bRet = ( nObjIdentifier != OBJ_GRAF && nObjIdentifier != OBJ_OLE2 );    // everything else
                break;
            default:
                OSL_FAIL("unknown content type");
        }
        return bRet;
    }

    void ScContentTree::GetDrawNames( ScContentId nType )
    {
        if ( nRootType != ScContentId::ROOT && nRootType != nType )              // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        // iterate in flat mode for groups
        SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;

        ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
        SfxObjectShell* pShell = pDoc->GetDocumentShell();
        if (pDrawLayer && pShell)
        {
            SCTAB nTabCount = pDoc->GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount; nTab++)
            {
                SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
                OSL_ENSURE(pPage,"Page ?");
                if (pPage)
                {
                    SdrObjListIter aIter( pPage, eIter );
                    SdrObject* pObject = aIter.Next();
                    while (pObject)
                    {
                        if ( IsPartOfType( nType, pObject->GetObjIdentifier() ) )
                        {
                            OUString aName = ScDrawLayer::GetVisibleName( pObject );
                            if (!aName.isEmpty())
                            {
                                if( bisInNavigatoeDlg )
                                {
                                    SvTreeListEntry* pParent = pRootNodes[nType];
                                    if (pParent)
                                    {
                                        InsertEntry( aName, pParent );
                                    }//end if parent
                                    else
                                        SAL_WARN("sc", "InsertContent without parent");
                                }
                            }

                        }

                        pObject = aIter.Next();
                    }
                }
            }
        }
    }

    void ScContentTree::GetGraphicNames()
    {
        GetDrawNames( ScContentId::GRAPHIC );
    }

    void ScContentTree::GetOleNames()
    {
        GetDrawNames( ScContentId::OLEOBJECT );
    }

    void ScContentTree::GetDrawingNames()
    {
        GetDrawNames( ScContentId::DRAWING );
    }

    void ScContentTree::GetLinkNames()
    {
        if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::AREALINK )                // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
        OSL_ENSURE(pLinkManager, "no LinkManager on document?");
        const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
        sal_uInt16 nCount = rLinks.size();
        for (sal_uInt16 i=0; i<nCount; i++)
        {
            ::sfx2::SvBaseLink* pBase = rLinks[i].get();
            if (dynamic_cast<const ScAreaLink*>( pBase) !=  nullptr)
                InsertContent( ScContentId::AREALINK, static_cast<ScAreaLink*>(pBase)->GetSource() );

                //  insert in list the names of source areas
        }
    }

    const ScAreaLink* ScContentTree::GetLink( sal_uLong nIndex )
    {
        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return nullptr;

        sal_uLong nFound = 0;
        sfx2::LinkManager* pLinkManager = pDoc->GetLinkManager();
        OSL_ENSURE(pLinkManager, "no LinkManager on document?");
        const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
        sal_uInt16 nCount = rLinks.size();
        for (sal_uInt16 i=0; i<nCount; i++)
        {
            ::sfx2::SvBaseLink* pBase = rLinks[i].get();
            if (auto pAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
            {
                if (nFound == nIndex)
                    return pAreaLink;
                ++nFound;
            }
        }

        OSL_FAIL("link not found");
        return nullptr;
    }

    static OUString lcl_NoteString( const ScPostIt& rNote )
    {
        OUString aText = rNote.GetText();
        sal_Int32 nAt;
        while ( (nAt = aText.indexOf( '\n' )) != -1 )
            aText = aText.replaceAt( nAt, 1, " " );
        return aText;
    }

    void ScContentTree::GetNoteStrings()
    {
        if ( nRootType != ScContentId::ROOT && nRootType != ScContentId::NOTE )        // hidden ?
            return;

        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return;

        // loop over cell notes
        std::vector<sc::NoteEntry> aEntries;
        pDoc->GetAllNoteEntries(aEntries);
        std::vector<sc::NoteEntry>::const_iterator it = aEntries.begin(), itEnd = aEntries.end();
        for (; it != itEnd; ++it)
            InsertContent(ScContentId::NOTE, lcl_NoteString(*it->mpNote));
    }

    ScAddress ScContentTree::GetNotePos( sal_uLong nIndex )
    {
        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return ScAddress();

        return pDoc->GetNotePosition(nIndex);
    }

    bool ScContentTree::NoteStringsChanged()
    {
        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return false;

        SvTreeListEntry* pParent = pRootNodes[ScContentId::NOTE];
        if (!pParent)
            return false;

        SvTreeListEntry* pEntry = FirstChild( pParent );

        std::vector<sc::NoteEntry> aEntries;
        pDoc->GetAllNoteEntries(aEntries);
        std::vector<sc::NoteEntry>::const_iterator it = aEntries.begin(), itEnd = aEntries.end();
        for (; it != itEnd; ++it)
        {
            const ScPostIt* pNote = it->mpNote;
            if (!pEntry)
                return true;

            if (lcl_NoteString(*pNote) != GetEntryText(pEntry))
                return true;

            pEntry = pEntry->NextSibling();
        }

        return pEntry != nullptr;
    }

    bool ScContentTree::DrawNamesChanged( ScContentId nType )
    {
        ScDocument* pDoc = GetSourceDocument();
        if (!pDoc)
            return false;

        SvTreeListEntry* pParent = pRootNodes[nType];
        if (!pParent)
            return false;

        SvTreeListEntry* pEntry = FirstChild( pParent );

        // iterate in flat mode for groups
        SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;

        bool bEqual = true;
        ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
        SfxObjectShell* pShell = pDoc->GetDocumentShell();
        if (pDrawLayer && pShell)
        {
            SCTAB nTabCount = pDoc->GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount && bEqual; nTab++)
            {
                SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
                OSL_ENSURE(pPage,"Page ?");
                if (pPage)
                {
                    SdrObjListIter aIter( pPage, eIter );
                    SdrObject* pObject = aIter.Next();
                    while (pObject && bEqual)
                    {
                        if ( IsPartOfType( nType, pObject->GetObjIdentifier() ) )
                        {
                            if ( !pEntry )
                                bEqual = false;
                            else
                            {
                                if ( ScDrawLayer::GetVisibleName( pObject ) != GetEntryText(pEntry) )
                                    bEqual = false;

                                pEntry = pEntry->NextSibling();
                            }
                        }
                        pObject = aIter.Next();
                    }
                }
            }
        }

        if ( pEntry )
            bEqual = false;             // anything else

        return !bEqual;
    }

    static bool lcl_GetRange( const ScDocument* pDoc, ScContentId nType, const OUString& rName, ScRange& rRange )
    {
        bool bFound = false;

        if ( nType == ScContentId::RANGENAME )
        {
            ScRangeName* pList = pDoc->GetRangeName();
            if (pList)
            {
                const ScRangeData* p = pList->findByUpperName(ScGlobal::pCharClass->uppercase(rName));
                if (p && p->IsValidReference(rRange))
                    bFound = true;
            }
        }
        else if ( nType == ScContentId::DBAREA )
        {
            ScDBCollection* pList = pDoc->GetDBCollection();
            if (pList)
            {
                const ScDBData* p = pList->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rName));
                if (p)
                {
                    SCTAB nTab;
                    SCCOL nCol1, nCol2;
                    SCROW nRow1, nRow2;
                    p->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
                    rRange = ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab);
                    bFound = true;
                }
            }
        }

        return bFound;
    }

    static void lcl_DoDragObject( ScDocShell* pSrcShell, const OUString& rName, ScContentId nType, vcl::Window* pWin )
    {
        ScDocument& rSrcDoc = pSrcShell->GetDocument();
        ScDrawLayer* pModel = rSrcDoc.GetDrawLayer();
        if (pModel)
        {
            bool bOle = ( nType == ScContentId::OLEOBJECT );
            bool bGraf = ( nType == ScContentId::GRAPHIC );
            sal_uInt16 nDrawId = sal::static_int_cast<sal_uInt16>( bOle ? OBJ_OLE2 : ( bGraf ? OBJ_GRAF : OBJ_GRUP ) );
            SCTAB nTab = 0;
            SdrObject* pObject = pModel->GetNamedObject( rName, nDrawId, nTab );
            if (pObject)
            {
                SdrView aEditView(*pModel);
                aEditView.ShowSdrPage(aEditView.GetModel()->GetPage(nTab));
                SdrPageView* pPV = aEditView.GetSdrPageView();
                aEditView.MarkObj(pObject, pPV);

                std::unique_ptr<SdrModel> pDragModel(aEditView.CreateMarkedObjModel());

                TransferableObjectDescriptor aObjDesc;
                pSrcShell->FillTransferableObjectDescriptor( aObjDesc );
                aObjDesc.maDisplayName = pSrcShell->GetMedium()->GetURLObject().GetURLNoPass();
                // maSize is set in ScDrawTransferObj ctor

                rtl::Reference<ScDrawTransferObj> pTransferObj = new ScDrawTransferObj( std::move(pDragModel), pSrcShell, aObjDesc );

                pTransferObj->SetDragSourceObj( *pObject, nTab );
                pTransferObj->SetDragSourceFlags(ScDragSrc::Navigator);

                SC_MOD()->SetDragObject( nullptr, pTransferObj.get() );
                pWin->ReleaseMouse();
                pTransferObj->StartDrag( pWin, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
            }
        }
    }

    static void lcl_DoDragCells( ScDocShell* pSrcShell, const ScRange& rRange, ScDragSrc nFlags, vcl::Window* pWin )
    {
        ScMarkData aMark;
        aMark.SelectTable( rRange.aStart.Tab(), true );
        aMark.SetMarkArea( rRange );

        ScDocument& rSrcDoc = pSrcShell->GetDocument();
        if ( !rSrcDoc.HasSelectedBlockMatrixFragment( rRange.aStart.Col(), rRange.aStart.Row(),
                                                       rRange.aEnd.Col(),   rRange.aEnd.Row(),
                                                       aMark ) )
        {
            ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
            ScClipParam aClipParam(rRange, false);
            rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aMark, false, false);
            // pClipDoc->ExtendMerge( rRange, sal_True );

            TransferableObjectDescriptor aObjDesc;
            pSrcShell->FillTransferableObjectDescriptor( aObjDesc );
            aObjDesc.maDisplayName = pSrcShell->GetMedium()->GetURLObject().GetURLNoPass();
            // maSize is set in ScTransferObj ctor

            rtl::Reference<ScTransferObj> pTransferObj = new ScTransferObj( std::move(pClipDoc), aObjDesc );

            pTransferObj->SetDragSource( pSrcShell, aMark );
            pTransferObj->SetDragSourceFlags( nFlags );

            SC_MOD()->SetDragObject( pTransferObj.get(), nullptr );      // for internal D&D
            pWin->ReleaseMouse();
            pTransferObj->StartDrag( pWin, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
        }
    }

    void ScContentTree::DoDrag()
    {
        std::unique_ptr<ScDocumentLoader> pDocLoader;
        bIsInDrag = true;

        ScModule* pScMod = SC_MOD();

        ScContentId nType;
        sal_uLong nChild;
        SvTreeListEntry* pEntry = GetCurEntry();
        GetEntryIndexes( nType, nChild, pEntry );

        if( pEntry &&
            (nChild != SC_CONTENT_NOCHILD) &&
            (nType != ScContentId::ROOT) &&
            (nType != ScContentId::NOTE) &&
            (nType != ScContentId::AREALINK) )
        {
            OUString aText( GetEntryText( pEntry ) );

            ScDocument* pLocalDoc = nullptr;                   // for URL drop
            OUString aDocName;
            if (bHiddenDoc)
                aDocName = aHiddenName;
            else
            {
                ScDocShell* pDocSh = GetManualOrCurrent();
                if (pDocSh)
                {
                    if (pDocSh->HasName())
                        aDocName = pDocSh->GetMedium()->GetName();
                    else
                        pLocalDoc = &pDocSh->GetDocument();      // drop only in this document
                }
            }

            bool bDoLinkTrans = false;      // use ScLinkTransferObj
            OUString aLinkURL;                // for ScLinkTransferObj
            OUString aLinkText;

            sal_uInt16 nDropMode = pParentWindow->GetDropMode();
            switch ( nDropMode )
            {
                case SC_DROPMODE_URL:
                    {
                        OUString aUrl = aDocName + "#" + aText;

                        pScMod->SetDragJump( pLocalDoc, aUrl, aText );

                        if (!aDocName.isEmpty())
                        {
                            //  provide URL to outside only if the document has a name
                            //  (without name, only internal D&D via SetDragJump)

                            aLinkURL = aUrl;
                            aLinkText = aText;
                        }
                        bDoLinkTrans = true;
                    }
                    break;
                case SC_DROPMODE_LINK:
                    {
                        if ( !aDocName.isEmpty() )           // link only to named documents
                        {
                            // for internal D&D, set flag to insert a link

                            switch ( nType )
                            {
                                case ScContentId::TABLE:
                                    pScMod->SetDragLink( aDocName, aText, EMPTY_OUSTRING );
                                    bDoLinkTrans = true;
                                    break;
                                case ScContentId::RANGENAME:
                                case ScContentId::DBAREA:
                                    pScMod->SetDragLink( aDocName, EMPTY_OUSTRING, aText );
                                    bDoLinkTrans = true;
                                    break;

                                // other types cannot be linked
                                default: break;
                            }
                        }
                    }
                    break;
                case SC_DROPMODE_COPY:
                    {
                        ScDocShell* pSrcShell = nullptr;
                        if ( bHiddenDoc )
                        {
                            OUString aFilter, aOptions;
                            OUString aURL = aHiddenName;
                            pDocLoader.reset(new ScDocumentLoader( aURL, aFilter, aOptions ));
                            if (!pDocLoader->IsError())
                                pSrcShell = pDocLoader->GetDocShell();
                        }
                        else
                            pSrcShell = GetManualOrCurrent();

                        if ( pSrcShell )
                        {
                            ScDocument& rSrcDoc = pSrcShell->GetDocument();
                            if ( nType == ScContentId::RANGENAME || nType == ScContentId::DBAREA )
                            {
                                ScRange aRange;
                                if ( lcl_GetRange( &rSrcDoc, nType, aText, aRange ) )
                                {
                                    lcl_DoDragCells( pSrcShell, aRange, ScDragSrc::Navigator, this );
                                }
                            }
                            else if ( nType == ScContentId::TABLE )
                            {
                                SCTAB nTab;
                                if ( rSrcDoc.GetTable( aText, nTab ) )
                                {
                                    ScRange aRange( 0,0,nTab, MAXCOL,MAXROW,nTab );
                                    lcl_DoDragCells( pSrcShell, aRange, (ScDragSrc::Navigator | ScDragSrc::Table), this );
                                }
                            }
                            else if ( nType == ScContentId::GRAPHIC || nType == ScContentId::OLEOBJECT ||
                                        nType == ScContentId::DRAWING )
                            {
                                lcl_DoDragObject( pSrcShell, aText, nType, this );

                                //  during ExecuteDrag the navigator can be deleted
                                //  -> don't access member anymore !!!
                            }
                        }
                    }
                    break;
            }

            if (bDoLinkTrans)
            {
                rtl::Reference<ScLinkTransferObj> pTransferObj = new ScLinkTransferObj;

                if ( !aLinkURL.isEmpty() )
                    pTransferObj->SetLinkURL( aLinkURL, aLinkText );

                //  SetDragJump / SetDragLink has been done above

                ReleaseMouse();
                pTransferObj->StartDrag( this, DND_ACTION_COPYMOVE | DND_ACTION_LINK );
            }
        }

        bIsInDrag = false;              // static member
    }

    IMPL_LINK_NOARG(ScContentTree, ExecDragHdl, void*, void)
    {
        //  as link, so that async without ImpMouseMoveMsg on the stack also
        //  the navigator can be deleted

        DoDrag();
    }

    void ScContentTree::LoadFile( const OUString& rUrl )
    {
        OUString aDocName = rUrl;
        sal_Int32 nPos = aDocName.indexOf('#');
        if ( nPos != -1 )
            aDocName = aDocName.copy(0, nPos);           // only the name without #...

        OUString aURL = aDocName;
        OUString aFilter, aOptions;
        ScDocumentLoader aLoader( aURL, aFilter, aOptions );
        if ( !aLoader.IsError() )
        {
            bHiddenDoc = true;
            aHiddenName = aDocName;
            aHiddenTitle = aLoader.GetTitle();
            pHiddenDocument = aLoader.GetDocument();

            Refresh();                      // get content from loaded document

            pHiddenDocument = nullptr;

            pParentWindow->GetDocNames( &aHiddenTitle );            // fill list
        }

        //  document is closed again by ScDocumentLoader in dtor
    }

    void ScContentTree::InitWindowBits( bool bButtons )
    {
        WinBits nFlags = GetStyle() | WB_CLIPCHILDREN | WB_HSCROLL;
        if (bButtons)
            nFlags |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;

        SetStyle( nFlags );
    }

    void ScContentTree::SetRootType( ScContentId nNew )
    {
        if ( nNew != nRootType )
        {
            nRootType = nNew;
            InitWindowBits( nNew == ScContentId::ROOT );
            Refresh();

            ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
            rCfg.SetRootType( nRootType );
        }
    }

    void ScContentTree::ToggleRoot()        // after selection
    {
        ScContentId nNew = ScContentId::ROOT;
        if ( nRootType == ScContentId::ROOT )
        {
            SvTreeListEntry* pEntry = GetCurEntry();
            if (pEntry)
            {
                SvTreeListEntry* pParent = GetParent(pEntry);
                for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
                    if ( pEntry == pRootNodes[static_cast<ScContentId>(i)] || pParent == pRootNodes[static_cast<ScContentId>(i)] )
                        nNew = static_cast<ScContentId>(i);
            }
        }

        SetRootType( nNew );
    }

    void ScContentTree::ResetManualDoc()
    {
        aManualDoc.clear();
        bHiddenDoc = false;

        ActiveDocChanged();
    }

    void ScContentTree::ActiveDocChanged()
    {
        if ( !bHiddenDoc && aManualDoc.isEmpty() )
            Refresh();                                  // content only if automatic

            //  if flag active Listbox must be updated

        OUString aCurrent;
        if ( bHiddenDoc )
            aCurrent = aHiddenTitle;
        else
        {
            ScDocShell* pSh = GetManualOrCurrent();
            if (pSh)
                aCurrent = pSh->GetTitle();
            else
            {
                //  document is no longer available

                aManualDoc.clear();             // again automatically
                Refresh();
                pSh = GetManualOrCurrent();     // should be active now
                if (pSh)
                    aCurrent = pSh->GetTitle();
            }
        }
        pParentWindow->GetDocNames( &aCurrent );        // select
    }

    void ScContentTree::SetManualDoc(const OUString& rName)
    {
        aManualDoc = rName;
        if (!bHiddenDoc)
        {
            Refresh();
            pParentWindow->GetDocNames( &aManualDoc );      // select
        }
    }

    void ScContentTree::SelectDoc(const OUString& rName)      // rName like shown in Menu/Listbox
    {
        if ( rName == pParentWindow->aStrActiveWin )
        {
            ResetManualDoc();
            return;
        }

        //  omit "active" or "inactive"

        OUString aRealName = rName;
        sal_Int32 nLen = rName.getLength();
        sal_Int32 nActiveStart = nLen - pParentWindow->aStrActive.getLength();
        if ( rName.copy( nActiveStart ) == pParentWindow->aStrActive )
            aRealName = rName.copy( 0, nActiveStart );
        sal_Int32 nNotActiveStart = nLen - pParentWindow->aStrNotActive.getLength();
        if ( rName.copy( nNotActiveStart ) == pParentWindow->aStrNotActive )
            aRealName = rName.copy( 0, nNotActiveStart );

        bool bLoaded = false;

        // Is it a normally loaded document?

        SfxObjectShell* pSh = SfxObjectShell::GetFirst();
        while ( pSh && !bLoaded )
        {
            if ( dynamic_cast<const ScDocShell*>( pSh) !=  nullptr )
                if ( pSh->GetTitle() == aRealName )
                    bLoaded = true;
            pSh = SfxObjectShell::GetNext( *pSh );
        }

        if (bLoaded)
        {
            bHiddenDoc = false;
            SetManualDoc(aRealName);
        }
        else if (!aHiddenTitle.isEmpty())                // hidden selected
        {
            if (!bHiddenDoc)
                LoadFile(aHiddenName);
        }
        else
        {
            OSL_FAIL("SelectDoc: not found");
        }
    }

    void ScContentTree::SelectEntryByName(const ScContentId nRoot, const OUString& rName)
    {
        SvTreeListEntry* pParent = pRootNodes[ nRoot ];

        if( !pParent->HasChildren() )
            return;

        SvTreeListEntry* pEntry = FirstChild( pParent );
        while( pEntry )
        {
            if( GetEntryText( pEntry ) == rName )
            {
                SvTreeListBox::Select( pEntry );

                // Scroll to the selected item
                if( SvTreeListBox::GetVScroll()->IsVisible() )
                {
                    long nBeforeCount = 0;
                    SvTreeList* pList = GetModel();
                    SvTreeListEntry* pRoot = pList->First();
                    while( pRoot != pParent )
                    {
                        ++nBeforeCount;
                        pRoot = pList->Next( pRoot );
                    }
                    SvTreeListBox::ScrollToAbsPos( pEntry->GetChildListPos()
                                + nBeforeCount );
                }

                return;
            }
            pEntry = Next( pEntry );
        }
    }

    void ScContentTree::ApplyNavigatorSettings()
    {
        const ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
        if( pSettings )
        {
            ScContentId nRootSel = pSettings->GetRootSelected();
            sal_uLong nChildSel = pSettings->GetChildSelected();

            for( int i = 1; i <= int(ScContentId::LAST); ++i )
            {
                ScContentId nEntry = static_cast<ScContentId>(i);
                if( pRootNodes[ nEntry ] )
                {
                    // expand
                    bool bExp = pSettings->IsExpanded( nEntry );
                    if( bExp != IsExpanded( pRootNodes[ nEntry ] ) )
                    {
                        if( bExp )
                            Expand( pRootNodes[ nEntry ] );
                        else
                            Collapse( pRootNodes[ nEntry ] );
                    }

                    // select
                    if( nRootSel == nEntry )
                    {
                        SvTreeListEntry* pEntry = nullptr;
                        if( bExp && (nChildSel != SC_CONTENT_NOCHILD) )
                            pEntry = GetEntry( pRootNodes[ nEntry ], nChildSel );
                        Select( pEntry ? pEntry : pRootNodes[ nEntry ] );
                    }
                }
            }
        }
    }

    void ScContentTree::StoreNavigatorSettings() const
    {
        ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
        if( pSettings )
        {
            for( int i = 1; i <= int(ScContentId::LAST); ++i )
            {
                ScContentId nEntry = static_cast<ScContentId>(i);
                bool bExp = pRootNodes[ nEntry ] && IsExpanded( pRootNodes[ nEntry ] );
                pSettings->SetExpanded( nEntry, bExp );
            }
            ScContentId nRoot;
            sal_uLong nChild;
            GetEntryIndexes( nRoot, nChild, GetCurEntry() );
            pSettings->SetRootSelected( nRoot );
            pSettings->SetChildSelected( nChild );
        }
    }

    void ScContentTree::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);
    }

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

↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.

↑ V1029 Numeric Truncation Error. Result of the 'size' function is written to the 16-bit variable.