/* -*- 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.