/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <string_view>
#include <svx/svditer.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdview.hxx>
#include <sfx2/linkmgr.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/viewfrm.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/svapp.hxx>
#include <osl/diagnose.h>
#include <tools/urlobj.hxx>
#include <sal/log.hxx>
#include <unotools/charclass.hxx>
#include <content.hxx>
#include <navipi.hxx>
#include <global.hxx>
#include <docsh.hxx>
#include <docfunc.hxx>
#include <scmod.hxx>
#include <rangenam.hxx>
#include <dbdata.hxx>
#include <drwlayer.hxx>
#include <transobj.hxx>
#include <drwtrans.hxx>
#include <lnktrans.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>
using namespace com::sun::star;
// order of the categories in navigator -------------------------------------
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
};
constexpr OUString 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
};
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(std::unique_ptr<weld::TreeView> xTreeView, ScNavigatorDlg* pNavigatorDlg)
: m_xTreeView(std::move(xTreeView))
, m_xScratchIter(m_xTreeView->make_iterator())
, m_xTransferObj(new ScLinkTransferObj)
, pParentWindow(pNavigatorDlg)
, nRootType(ScContentId::ROOT)
, bIsInNavigatorDlg(false)
, m_bFreeze(false)
, m_nAsyncMouseReleaseId(nullptr)
{
for (sal_uInt16 i = 0; i <= int(ScContentId::LAST); ++i)
pPosList[pTypeList[i]] = i; // inverse for searching
m_aRootNodes[ScContentId::ROOT] = nullptr;
for (sal_uInt16 i = 1; i < int(ScContentId::LAST); ++i)
InitRoot(static_cast<ScContentId>(i));
m_xTreeView->connect_row_activated(LINK(this, ScContentTree, ContentDoubleClickHdl));
m_xTreeView->connect_mouse_release(LINK(this, ScContentTree, MouseReleaseHdl));
m_xTreeView->connect_key_press(LINK(this, ScContentTree, KeyInputHdl));
m_xTreeView->connect_popup_menu(LINK(this, ScContentTree, CommandHdl));
m_xTreeView->connect_query_tooltip(LINK(this, ScContentTree, QueryTooltipHdl));
rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
m_xTreeView->connect_drag_begin(LINK(this, ScContentTree, DragBeginHdl));
m_xTreeView->set_selection_mode( SelectionMode::Single );
m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30,
m_xTreeView->get_text_height() * 13);
}
ScContentTree::~ScContentTree()
{
if (m_nAsyncMouseReleaseId)
{
Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
m_nAsyncMouseReleaseId = nullptr;
}
}
const TranslateId 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 ?
{
m_aRootNodes[nType] = nullptr;
return;
}
auto const & aImage = aContentBmps[static_cast<int>(nType) - 1];
OUString aName;
if(comphelper::LibreOfficeKit::isActive())
{
//In case of LOK we may have many different ScContentTrees in different languages.
//At creation time, we store what language we use, and then use it later too.
//It does not work in the constructor, that is why it is here.
if (!m_pResLocaleForLOK)
{
m_pResLocaleForLOK = std::make_unique<std::locale>(SC_MOD()->GetResLocale());
}
aName = Translate::get(SCSTR_CONTENT_ARY[static_cast<int>(nType)], *m_pResLocaleForLOK);
}
else
{
aName = ScResId(SCSTR_CONTENT_ARY[static_cast<int>(nType)]);
}
// back to the correct position:
sal_uInt16 nPos = nRootType != ScContentId::ROOT ? 0 : pPosList[nType]-1;
m_aRootNodes[nType] = m_xTreeView->make_iterator();
m_xTreeView->insert(nullptr, nPos, &aName, nullptr, nullptr, nullptr, false, m_aRootNodes[nType].get());
m_xTreeView->set_image(*m_aRootNodes[nType], aImage);
}
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 m_xTreeView->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 bWasFrozen = m_bFreeze;
if (!bWasFrozen)
freeze();
m_xTreeView->clear();
if (!bWasFrozen)
thaw();
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
{
weld::TreeIter* pParent = m_aRootNodes[nType].get();
if (!pParent || m_xTreeView->iter_has_child(*pParent)) // not if no children existing
{
if (pParent)
m_xTreeView->remove(*pParent); // with all children
InitRoot( nType ); // if needed insert anew
}
}
}
void ScContentTree::InsertContent( ScContentId nType, const OUString& rValue )
{
weld::TreeIter* pParent = m_aRootNodes[nType].get();
if (pParent)
{
m_xTreeView->insert(pParent, -1, &rValue, nullptr, nullptr, nullptr, false, m_xScratchIter.get());
m_xTreeView->set_sensitive(*m_xScratchIter, true);
}
else
{
OSL_FAIL("InsertContent without parent");
}
}
void ScContentTree::GetEntryIndexes(ScContentId& rnRootIndex, sal_uLong& rnChildIndex, const weld::TreeIter* pEntry) const
{
rnRootIndex = ScContentId::ROOT;
rnChildIndex = SC_CONTENT_NOCHILD;
if( !pEntry )
return;
std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(pEntry));
if (!m_xTreeView->iter_parent(*xParent))
xParent.reset();
bool bFound = false;
for( int i = 1; !bFound && (i <= int(ScContentId::LAST)); ++i )
{
ScContentId nRoot = static_cast<ScContentId>(i);
if (!m_aRootNodes[nRoot])
continue;
if (m_xTreeView->iter_compare(*pEntry, *m_aRootNodes[nRoot]) == 0)
{
rnRootIndex = nRoot;
rnChildIndex = ~0UL;
bFound = true;
}
else if (xParent && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[nRoot]) == 0)
{
rnRootIndex = nRoot;
// search the entry in all child entries of the parent
sal_uLong nEntry = 0;
std::unique_ptr<weld::TreeIter> xIterEntry(m_xTreeView->make_iterator(xParent.get()));
bool bIterEntry = m_xTreeView->iter_children(*xIterEntry);
while (!bFound && bIterEntry)
{
if (m_xTreeView->iter_compare(*pEntry, *xIterEntry) == 0)
{
rnChildIndex = nEntry;
bFound = true; // exit the while loop
}
bIterEntry = m_xTreeView->iter_next_sibling(*xIterEntry);
++nEntry;
}
bFound = true; // exit the for loop
}
}
}
sal_uLong ScContentTree::GetChildIndex(const weld::TreeIter* 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::getCharClass().uppercase(rDBName));
if (pData)
{
ScRange aRange;
pData->GetArea(aRange);
aRet = aRange.Format(*pDoc, ScRefFlags::RANGE_ABS_3D);
}
}
return aRet;
}
IMPL_LINK_NOARG(ScContentTree, ContentDoubleClickHdl, weld::TreeView&, bool)
{
ScContentId nType;
sal_uLong nChild;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xEntry.get()))
xEntry.reset();
GetEntryIndexes(nType, nChild, xEntry.get());
if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
{
OUString aText(m_xTreeView->get_text(*xEntry));
if ( !aManualDoc.isEmpty() )
pParentWindow->SetCurrentDoc( aManualDoc );
switch( nType )
{
case ScContentId::TABLE:
{
// tdf#133159 store current config before changing sheet
// plausible that this should be done for all cases, but this
// is the known case that needs it
StoreNavigatorSettings();
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() );
// Check whether the comment is currently visible and toggle its visibility
ScDocument* pSrcDoc = GetSourceDocument();
if (ScPostIt* pNote = pSrcDoc ? pSrcDoc->GetNote(aPos.Col(), aPos.Row(), aPos.Tab()) : nullptr)
{
bool bVisible = pNote->IsCaptionShown();
// Effectivelly set the visibility of the comment
GetManualOrCurrent()->GetDocFunc().ShowNote(aPos, !bVisible);
// Put the note in edit mode
ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
pScTabViewShell->EditNote();
}
}
break;
case ScContentId::AREALINK:
{
const ScAreaLink* pLink = GetLink(nChild);
ScDocument* pSrcDoc = GetSourceDocument();
if (pLink && pSrcDoc)
{
const ScRange& aRange = pLink->GetDestArea();
OUString aRangeStr(aRange.Format(*pSrcDoc, ScRefFlags::RANGE_ABS_3D, pSrcDoc->GetAddressConvention()));
pParentWindow->SetCurrentCellStr( aRangeStr );
}
}
break;
default: break;
}
ScNavigatorDlg::ReleaseFocus(); // set focus into document
}
return false;
}
void ScContentTree::LaunchAsyncStoreNavigatorSettings()
{
if (!m_nAsyncMouseReleaseId)
m_nAsyncMouseReleaseId = Application::PostUserEvent(LINK(this, ScContentTree, AsyncStoreNavigatorSettings));
}
IMPL_LINK_NOARG(ScContentTree, MouseReleaseHdl, const MouseEvent&, bool)
{
LaunchAsyncStoreNavigatorSettings();
return false;
}
IMPL_LINK_NOARG(ScContentTree, AsyncStoreNavigatorSettings, void*, void)
{
m_nAsyncMouseReleaseId = nullptr;
StoreNavigatorSettings();
}
IMPL_LINK(ScContentTree, KeyInputHdl, const KeyEvent&, rKEvt, bool)
{
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:
{
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xEntry.get()))
xEntry.reset();
if (xEntry)
{
ScContentId nType;
sal_uLong nChild;
GetEntryIndexes(nType, nChild, xEntry.get());
if (nType != ScContentId::ROOT && nChild == SC_CONTENT_NOCHILD)
{
if (m_xTreeView->get_row_expanded(*xEntry))
m_xTreeView->collapse_row(*xEntry);
else
m_xTreeView->expand_row(*xEntry);
}
else
ContentDoubleClickHdl(*m_xTreeView); // select content as if double clicked
}
bUsed = true;
}
break;
}
}
//Make KEY_SPACE has same function as DoubleClick, and realize
//multi-selection.
if ( bIsInNavigatorDlg )
{
if(aCode.GetCode() == KEY_SPACE )
{
bUsed = true;
ScContentId nType;
sal_uLong nChild;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xEntry.get()))
xEntry.reset();
GetEntryIndexes(nType, nChild, xEntry.get());
if (xEntry && (nType != ScContentId::ROOT) && (nChild != SC_CONTENT_NOCHILD))
{
OUString aText(m_xTreeView->get_text(*xEntry));
if (!aManualDoc.isEmpty())
pParentWindow->SetCurrentDoc( aManualDoc );
switch (nType)
{
case ScContentId::OLEOBJECT:
case ScContentId::GRAPHIC:
case ScContentId::DRAWING:
{
ScDrawView* pScDrawView = nullptr;
ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
if (pScTabViewShell)
pScDrawView = pScTabViewShell->GetViewData().GetScDrawView();
if (pScDrawView)
{
pScDrawView->SelectCurrentViewObject(aText);
bool bHasMakredObject = false;
weld::TreeIter* pParent = m_aRootNodes[nType].get();
std::unique_ptr<weld::TreeIter> xBeginEntry(m_xTreeView->make_iterator(pParent));
bool bBeginEntry = false;
if (pParent)
bBeginEntry = m_xTreeView->iter_children(*xBeginEntry);
while (bBeginEntry)
{
OUString aTempText(m_xTreeView->get_text(*xBeginEntry));
if( pScDrawView->GetObjectIsMarked( pScDrawView->GetObjectByName( aTempText ) ) )
{
bHasMakredObject = true;
break;
}
bBeginEntry = m_xTreeView->iter_next(*xBeginEntry);
}
if (!bHasMakredObject && pScTabViewShell)
pScTabViewShell->SetDrawShell(false);
}
break;
}
default:
break;
}
}
}
}
if (!bUsed)
{
if (aCode.GetCode() == KEY_F5)
StoreNavigatorSettings();
else
LaunchAsyncStoreNavigatorSettings();
}
return bUsed;
}
IMPL_LINK(ScContentTree, CommandHdl, const CommandEvent&, rCEvt, bool)
{
bool bDone = false;
ScContentId nType;
sal_uLong nChild;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xEntry.get()))
xEntry.reset();
GetEntryIndexes(nType, nChild, xEntry.get());
switch ( rCEvt.GetCommand() )
{
case CommandEventId::ContextMenu:
{
// drag-and-drop mode
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), u"modules/scalc/ui/dropmenu.ui"_ustr));
std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu(u"contextmenu"_ustr));
std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu(u"dragmodesubmenu"_ustr));
switch (pParentWindow->GetDropMode())
{
case 0:
xDropMenu->set_active(u"hyperlink"_ustr, true);
break;
case 1:
xDropMenu->set_active(u"link"_ustr, true);
break;
case 2:
xDropMenu->set_active(u"copy"_ustr, true);
break;
}
// displayed document
std::unique_ptr<weld::Menu> xDocMenu(xBuilder->weld_menu(u"displaymenu"_ustr));
sal_uInt16 i=0;
OUString sActive;
OUString sId;
// 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;
sId = "document" + OUString::number(i);
xDocMenu->append_radio(sId, aEntry);
if (aName == aManualDoc)
sActive = sId;
}
pSh = SfxObjectShell::GetNext( *pSh );
}
// "active window"
++i;
sId = "document" + OUString::number(i);
xDocMenu->append_radio(sId, pParentWindow->aStrActiveWin);
if (aManualDoc.isEmpty())
sActive = sId;
xDocMenu->set_active(sActive, true);
// Edit/Delete Comments are only visible for comments
if (nType != ScContentId::NOTE)
{
xPop->set_visible(u"edit"_ustr, false);
xPop->set_visible(u"delete"_ustr, false);
}
OUString sIdent = xPop->popup_at_rect(m_xTreeView.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)));
if (sIdent == "hyperlink")
pParentWindow->SetDropMode(0);
else if (sIdent == "link")
pParentWindow->SetDropMode(1);
else if (sIdent == "copy")
pParentWindow->SetDropMode(2);
else if (sIdent.startsWith("document"))
{
OUString aName = xDocMenu->get_label(sIdent);
SelectDoc(aName);
}
else if (sIdent == "edit")
{
ScAddress aPos = GetNotePos( nChild );
pParentWindow->SetCurrentTable( aPos.Tab() );
pParentWindow->SetCurrentCell( aPos.Col(), aPos.Row() );
ScDocument* pSrcDoc = GetSourceDocument();
if (pSrcDoc->GetNote(aPos.Col(), aPos.Row(), aPos.Tab()))
{
// Make the note visible and put it in edit mode
GetManualOrCurrent()->GetDocFunc().ShowNote(aPos, true);
ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
pScTabViewShell->EditNote();
bDone = true;
}
}
else if (sIdent == "delete")
{
ScAddress aPos = GetNotePos(nChild);
pParentWindow->SetCurrentTable(aPos.Tab());
pParentWindow->SetCurrentCell(aPos.Col(), aPos.Row());
ScTabViewShell* pScTabViewShell = ScNavigatorDlg::GetTabViewShell();
pScTabViewShell->DeleteContents(InsertDeleteFlags::NOTE);
}
}
break;
default: break;
}
return bDone;
}
IMPL_LINK(ScContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString)
{
OUString aHelpText;
std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
if (!m_xTreeView->iter_parent(*xParent))
xParent.reset();
if (!xParent) // Top-Level ?
{
aHelpText = OUString::number(m_xTreeView->iter_n_children(rEntry)) +
" " + m_xTreeView->get_text(rEntry);
}
else if (m_aRootNodes[ScContentId::NOTE] && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[ScContentId::NOTE]) == 0)
{
aHelpText = m_xTreeView->get_text(rEntry); // notes as help text
}
else if (m_aRootNodes[ScContentId::AREALINK] && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[ScContentId::AREALINK]) == 0)
{
auto nIndex = GetChildIndex(&rEntry);
if (nIndex != SC_CONTENT_NOCHILD)
{
const ScAreaLink* pLink = GetLink(nIndex);
if (pLink)
{
aHelpText = pLink->GetFile(); // source file as help text
}
}
}
return aHelpText;
}
ScDocument* ScContentTree::GetSourceDocument()
{
ScDocShell* pSh = GetManualOrCurrent();
if (pSh)
return &pSh->GetDocument();
return nullptr;
}
void ScContentTree::Refresh( ScContentId nType )
{
// 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;
freeze();
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();
thaw();
ApplyNavigatorSettings();
}
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(std::u16string_view rName, std::u16string_view rTableName)
{
return OUString::Concat(rName) + " (" + rTableName + ")";
}
}
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();
for (const auto& rEntry : *pRangeNames)
{
if (rEntry.second->IsValidReference(aDummy))
aSet.insert(rEntry.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 (const auto& rEntry : *pLocalRangeName)
{
if (rEntry.second->IsValidReference(aDummy))
aSet.insert(createLocalRangeName(rEntry.second->GetName(), aTableName));
}
}
}
for (const auto& rItem : aSet)
{
InsertContent(ScContentId::RANGENAME, rItem);
}
}
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();
for (const auto& rxDB : rDBs)
{
const OUString& aStrName = rxDB->GetName();
InsertContent(ScContentId::DBAREA, aStrName);
}
}
bool ScContentTree::IsPartOfType( ScContentId nContentType, SdrObjKind nObjIdentifier )
{
bool bRet = false;
switch ( nContentType )
{
case ScContentId::GRAPHIC:
bRet = ( nObjIdentifier == SdrObjKind::Graphic );
break;
case ScContentId::OLEOBJECT:
bRet = ( nObjIdentifier == SdrObjKind::OLE2 );
break;
case ScContentId::DRAWING:
bRet = ( nObjIdentifier != SdrObjKind::Graphic && nObjIdentifier != SdrObjKind::OLE2 ); // everything else
break;
default:
OSL_FAIL("unknown content type");
}
return bRet;
}
constexpr int MAX_TREE_NODES = 1000;
void ScContentTree::GetDrawNames( ScContentId nType )
{
if (!bIsInNavigatorDlg)
return;
if ( nRootType != ScContentId::ROOT && nRootType != nType ) // hidden ?
return;
ScDocument* pDoc = GetSourceDocument();
if (!pDoc)
return;
ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
if (!pDrawLayer)
return;
ScDocShell* pShell = pDoc->GetDocumentShell();
if (!pShell)
return;
// iterate in flat mode for groups
SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
std::vector<OUString> aNames;
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)
continue;
SdrObjListIter aIter(pPage, eIter);
SdrObject* pObject = aIter.Next();
while (pObject)
{
if (IsPartOfType(nType, pObject->GetObjIdentifier()))
{
OUString aName = ScDrawLayer::GetVisibleName(pObject);
if (!aName.isEmpty())
aNames.push_back(aName);
if (aNames.size() > MAX_TREE_NODES)
{
SAL_WARN("sc", "too many tree nodes, ignoring the rest");
break;
}
}
pObject = aIter.Next();
}
}
weld::TreeIter* pParent = m_aRootNodes[nType].get();
assert(pParent && "InsertContent without parent");
// insert all of these in one go under pParent
m_xTreeView->bulk_insert_for_each(aNames.size(), [this, &aNames](weld::TreeIter& rIter, int nIndex) {
m_xTreeView->set_text(rIter, aNames[nIndex], 0);
m_xTreeView->set_sensitive(rIter, true);
}, pParent);
}
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();
assert(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 pScAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
InsertContent( ScContentId::AREALINK, pScAreaLink->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();
assert(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 )
{
return rNote.GetText().replace('\n', ' ');
}
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);
weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
for (const auto& rEntry : aEntries)
{
OUString aValue = lcl_NoteString(*rEntry.mpNote);
m_xTreeView->insert(pParent, -1, &aValue, nullptr, nullptr, nullptr, false, m_xScratchIter.get());
m_xTreeView->set_sensitive(*m_xScratchIter, true);
}
}
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;
weld::TreeIter* pParent = m_aRootNodes[ScContentId::NOTE].get();
if (!pParent)
return false;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
bool bEntry = m_xTreeView->iter_children(*xEntry);
std::vector<sc::NoteEntry> aEntries;
pDoc->GetAllNoteEntries(aEntries);
for (const auto& rEntry : aEntries)
{
const ScPostIt* pNote = rEntry.mpNote;
if (!bEntry)
return true;
if (lcl_NoteString(*pNote) != m_xTreeView->get_text(*xEntry))
return true;
bEntry = m_xTreeView->iter_next_sibling(*xEntry);
}
return bEntry;
}
bool ScContentTree::DrawNamesChanged( ScContentId nType )
{
ScDocument* pDoc = GetSourceDocument();
if (!pDoc)
return false;
weld::TreeIter* pParent = m_aRootNodes[nType].get();
if (!pParent)
return false;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
bool bEntry = m_xTreeView->iter_children(*xEntry);
// iterate in flat mode for groups
SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups;
bool bEqual = true;
ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
ScDocShell* 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 ( !bEntry )
bEqual = false;
else
{
if (ScDrawLayer::GetVisibleName(pObject) != m_xTreeView->get_text(*xEntry))
bEqual = false;
bEntry = m_xTreeView->iter_next_sibling(*xEntry);
}
}
pObject = aIter.Next();
}
}
}
}
if ( bEntry )
bEqual = false; // anything else
return !bEqual;
}
static bool lcl_GetRange( const ScDocument& rDoc, ScContentId nType, const OUString& rName, ScRange& rRange )
{
bool bFound = false;
if ( nType == ScContentId::RANGENAME )
{
ScRangeName* pList = rDoc.GetRangeName();
if (pList)
{
const ScRangeData* p = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
if (p && p->IsValidReference(rRange))
bFound = true;
}
}
else if ( nType == ScContentId::DBAREA )
{
ScDBCollection* pList = rDoc.GetDBCollection();
if (pList)
{
const ScDBData* p = pList->getNamedDBs().findByUpperName(ScGlobal::getCharClass().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 bool lcl_DoDragObject( ScDocShell* pSrcShell, std::u16string_view rName, ScContentId nType, weld::TreeView& rTreeView )
{
bool bDisallow = true;
ScDocument& rSrcDoc = pSrcShell->GetDocument();
ScDrawLayer* pModel = rSrcDoc.GetDrawLayer();
if (pModel)
{
bool bOle = ( nType == ScContentId::OLEOBJECT );
bool bGraf = ( nType == ScContentId::GRAPHIC );
SdrObjKind nDrawId = bOle ? SdrObjKind::OLE2 : ( bGraf ? SdrObjKind::Graphic : SdrObjKind::Group );
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);
// tdf125520 this is a D&D-start potentially with an OLE object. If
// so, we need to do similar as e.g. in ScDrawView::BeginDrag so that
// the temporary SdrModel for transfer does have a GetPersist() so
// that the EmbeddedObjectContainer gets copied. We need no CheckOle
// here, test is simpler.
ScDocShellRef aDragShellRef;
if(SdrObjKind::OLE2 == pObject->GetObjIdentifier())
{
aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
aDragShellRef->DoInitNew();
}
ScDrawLayer::SetGlobalDrawPersist(aDragShellRef.get());
std::unique_ptr<SdrModel> pDragModel(aEditView.CreateMarkedObjModel());
ScDrawLayer::SetGlobalDrawPersist(nullptr);
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, std::move(aObjDesc) );
pTransferObj->SetDragSourceObj( *pObject, nTab );
pTransferObj->SetDragSourceFlags(ScDragSrc::Navigator);
SC_MOD()->SetDragObject( nullptr, pTransferObj.get() );
rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
bDisallow = false;
}
}
return bDisallow;
}
static bool lcl_DoDragCells( ScDocShell* pSrcShell, const ScRange& rRange, ScDragSrc nFlags, weld::TreeView& rTreeView )
{
bool bDisallow = true;
ScDocument& rSrcDoc = pSrcShell->GetDocument();
ScMarkData aMark(rSrcDoc.GetSheetLimits());
aMark.SelectTable( rRange.aStart.Tab(), true );
aMark.SetMarkArea( rRange );
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), std::move(aObjDesc) );
pTransferObj->SetDragSource( pSrcShell, aMark );
pTransferObj->SetDragSourceFlags( nFlags );
SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D
rtl::Reference<TransferDataContainer> xHelper(pTransferObj);
rTreeView.enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
bDisallow = false;
}
return bDisallow;
}
IMPL_LINK(ScContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
{
rUnsetDragIcon = true;
StoreNavigatorSettings();
bool bDisallow = true;
ScModule* pScMod = SC_MOD();
ScContentId nType;
sal_uLong nChild;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xEntry.get()))
xEntry.reset();
GetEntryIndexes(nType, nChild, xEntry.get());
if( xEntry &&
(nChild != SC_CONTENT_NOCHILD) &&
(nType != ScContentId::ROOT) &&
(nType != ScContentId::NOTE) &&
(nType != ScContentId::AREALINK) )
{
OUString aText(m_xTreeView->get_text(*xEntry));
ScDocument* pLocalDoc = nullptr; // for URL drop
OUString aDocName;
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, OUString() );
bDoLinkTrans = true;
break;
case ScContentId::RANGENAME:
case ScContentId::DBAREA:
pScMod->SetDragLink( aDocName, OUString(), aText );
bDoLinkTrans = true;
break;
// other types cannot be linked
default: break;
}
}
}
break;
case SC_DROPMODE_COPY:
{
ScDocShell* pSrcShell = GetManualOrCurrent();
if ( pSrcShell )
{
ScDocument& rSrcDoc = pSrcShell->GetDocument();
if ( nType == ScContentId::RANGENAME || nType == ScContentId::DBAREA )
{
ScRange aRange;
if ( lcl_GetRange( rSrcDoc, nType, aText, aRange ) )
{
bDisallow = lcl_DoDragCells( pSrcShell, aRange, ScDragSrc::Navigator, *m_xTreeView );
}
}
else if ( nType == ScContentId::TABLE )
{
SCTAB nTab;
if ( rSrcDoc.GetTable( aText, nTab ) )
{
ScRange aRange(0, 0, nTab, rSrcDoc.MaxCol(), rSrcDoc.MaxRow(), nTab);
bDisallow = lcl_DoDragCells( pSrcShell, aRange, (ScDragSrc::Navigator | ScDragSrc::Table), *m_xTreeView );
}
}
else if ( nType == ScContentId::GRAPHIC || nType == ScContentId::OLEOBJECT ||
nType == ScContentId::DRAWING )
{
bDisallow = lcl_DoDragObject( pSrcShell, aText, nType, *m_xTreeView );
// during ExecuteDrag the navigator can be deleted
// -> don't access member anymore !!!
}
}
}
break;
}
if (bDoLinkTrans)
{
if (!aLinkURL.isEmpty())
m_xTransferObj->SetLinkURL(aLinkURL, aLinkText);
rtl::Reference<TransferDataContainer> xHelper(m_xTransferObj);
m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
bDisallow = false;
}
}
return bDisallow;
}
void ScContentTree::SetRootType( ScContentId nNew )
{
if ( nNew != nRootType )
{
nRootType = nNew;
Refresh();
ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg();
rCfg.SetRootType( nRootType );
}
}
void ScContentTree::ToggleRoot() // after selection
{
ScContentId nNew = ScContentId::ROOT;
if ( nRootType == ScContentId::ROOT )
{
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
if (m_xTreeView->get_cursor(xEntry.get()))
{
std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(xEntry.get()));
if (!m_xTreeView->iter_parent(*xParent))
xParent.reset();
for (sal_uInt16 i=1; i<=int(ScContentId::LAST); i++)
{
if (!m_aRootNodes[static_cast<ScContentId>(i)])
continue;
if ((m_xTreeView->iter_compare(*xEntry, *m_aRootNodes[static_cast<ScContentId>(i)]) == 0) ||
(xParent && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[static_cast<ScContentId>(i)]) == 0))
{
nNew = static_cast<ScContentId>(i);
}
}
}
}
SetRootType( nNew );
}
void ScContentTree::ResetManualDoc()
{
aManualDoc.clear();
ActiveDocChanged();
}
bool ScContentTree::ActiveDocChanged()
{
bool bRefreshed = false;
if (aManualDoc.isEmpty())
{
Refresh(); // content only if automatic
bRefreshed = true;
}
// if flag active Listbox must be updated
OUString aCurrent;
ScDocShell* pSh = GetManualOrCurrent();
if (pSh)
aCurrent = pSh->GetTitle();
else
{
// document is no longer available
aManualDoc.clear(); // again automatically
Refresh();
bRefreshed = true;
pSh = GetManualOrCurrent(); // should be active now
if (pSh)
aCurrent = pSh->GetTitle();
}
pParentWindow->GetDocNames( &aCurrent ); // select
return bRefreshed;
}
void ScContentTree::SetManualDoc(const OUString& rName)
{
aManualDoc = rName;
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.subView( nActiveStart ) == pParentWindow->aStrActive )
aRealName = rName.copy( 0, nActiveStart );
sal_Int32 nNotActiveStart = nLen - pParentWindow->aStrNotActive.getLength();
if ( rName.subView( 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)
{
SetManualDoc(aRealName);
}
else
{
OSL_FAIL("SelectDoc: not found");
}
}
void ScContentTree::SelectEntryByName(const ScContentId nRoot, std::u16string_view rName)
{
weld::TreeIter* pParent = m_aRootNodes[nRoot].get();
if (!pParent || !m_xTreeView->iter_has_child(*pParent))
return;
std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pParent));
bool bEntry = m_xTreeView->iter_children(*xEntry);
while (bEntry)
{
if (m_xTreeView->get_text(*xEntry) == rName)
{
m_xTreeView->select(*xEntry);
m_xTreeView->set_cursor(*xEntry);
// Scroll to the selected item
m_xTreeView->scroll_to_row(*xEntry);
StoreNavigatorSettings();
return;
}
bEntry = m_xTreeView->iter_next(*xEntry);
}
}
void ScContentTree::ApplyNavigatorSettings()
{
const ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
if( !pSettings )
return;
ScContentId nRootSel = pSettings->GetRootSelected();
auto nChildSel = pSettings->GetChildSelected();
// tdf#133079 ensure Sheet root is selected if nothing
// else would be
if (nRootSel == ScContentId::ROOT)
{
nRootSel = ScContentId::TABLE;
nChildSel = SC_CONTENT_NOCHILD;
}
for( int i = 1; i <= int(ScContentId::LAST); ++i )
{
ScContentId nEntry = static_cast<ScContentId>(i);
if( m_aRootNodes[ nEntry ] )
{
// gray or ungray
if (!m_xTreeView->iter_has_child(*m_aRootNodes[nEntry]))
m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], false);
else
m_xTreeView->set_sensitive(*m_aRootNodes[nEntry], true);
// expand
bool bExp = pSettings->IsExpanded( nEntry );
if (bExp != m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]))
{
if( bExp )
m_xTreeView->expand_row(*m_aRootNodes[nEntry]);
else
m_xTreeView->collapse_row(*m_aRootNodes[nEntry]);
}
// select
if( nRootSel == nEntry )
{
std::unique_ptr<weld::TreeIter> xEntry;
if (bExp && (nChildSel != SC_CONTENT_NOCHILD))
{
xEntry = m_xTreeView->make_iterator(m_aRootNodes[nEntry].get());
if (!m_xTreeView->iter_children(*xEntry) || !m_xTreeView->iter_nth_sibling(*xEntry, nChildSel))
xEntry.reset();
}
m_xTreeView->select(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
m_xTreeView->set_cursor(xEntry ? *xEntry : *m_aRootNodes[nEntry]);
}
}
}
}
void ScContentTree::StoreNavigatorSettings()
{
if (m_nAsyncMouseReleaseId)
{
Application::RemoveUserEvent(m_nAsyncMouseReleaseId);
m_nAsyncMouseReleaseId = nullptr;
}
ScNavigatorSettings* pSettings = ScNavigatorDlg::GetNavigatorSettings();
if( !pSettings )
return;
for( int i = 1; i <= int(ScContentId::LAST); ++i )
{
ScContentId nEntry = static_cast<ScContentId>(i);
bool bExp = m_aRootNodes[nEntry] && m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]);
pSettings->SetExpanded( nEntry, bExp );
}
std::unique_ptr<weld::TreeIter> xCurEntry(m_xTreeView->make_iterator());
if (!m_xTreeView->get_cursor(xCurEntry.get()))
xCurEntry.reset();
ScContentId nRoot;
sal_uLong nChild;
GetEntryIndexes(nRoot, nChild, xCurEntry.get());
pSettings->SetRootSelected( nRoot );
pSettings->SetChildSelected( nChild );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.