From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sc/source/ui/navipi/content.cxx | 1637 ++++++++++++++++++++++++++++++++++++++ sc/source/ui/navipi/navcitem.cxx | 99 +++ sc/source/ui/navipi/navipi.cxx | 965 ++++++++++++++++++++++ sc/source/ui/navipi/scenwnd.cxx | 242 ++++++ 4 files changed, 2943 insertions(+) create mode 100644 sc/source/ui/navipi/content.cxx create mode 100644 sc/source/ui/navipi/navcitem.cxx create mode 100644 sc/source/ui/navipi/navipi.cxx create mode 100644 sc/source/ui/navipi/scenwnd.cxx (limited to 'sc/source/ui/navipi') diff --git a/sc/source/ui/navipi/content.cxx b/sc/source/ui/navipi/content.cxx new file mode 100644 index 000000000..2c8336fd3 --- /dev/null +++ b/sc/source/ui/navipi/content.cxx @@ -0,0 +1,1637 @@ +/* -*- 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 rtl::OUStringConstExpr 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 ); + while ( pObjSh && !pSh ) + { + if ( pObjSh->GetTitle() == aManualDoc ) + pSh = dynamic_cast( pObjSh ); + pObjSh = SfxObjectShell::GetNext( *pObjSh, checkSfxObjectShell ); + } + } + 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( pObjSh ); + } + } + + return pSh; +} + +// ScContentTree + +ScContentTree::ScContentTree(std::unique_ptr xTreeView, ScNavigatorDlg* pNavigatorDlg) + : m_xTreeView(std::move(xTreeView)) + , m_xScratchIter(m_xTreeView->make_iterator()) + , m_xTransferObj(new ScLinkTransferObj) + , pParentWindow(pNavigatorDlg) + , nRootType(ScContentId::ROOT) + , bHiddenDoc(false) + , pHiddenDocument(nullptr) + , 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(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 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(nType) - 1]); + OUString aName(ScResId(SCSTR_CONTENT_ARY[static_cast(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], OUString(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(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 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(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 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 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)) + { + if ( bHiddenDoc ) + return false; //! later... + + 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() ); + } + 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 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 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)) + { + if ( bHiddenDoc ) + return true; //! later... + 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 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; + + switch ( rCEvt.GetCommand() ) + { + case CommandEventId::ContextMenu: + { + // drag-and-drop mode + std::unique_ptr xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/scalc/ui/dropmenu.ui")); + std::unique_ptr xPop(xBuilder->weld_menu("contextmenu")); + std::unique_ptr xDropMenu(xBuilder->weld_menu("dragmodesubmenu")); + + switch (pParentWindow->GetDropMode()) + { + case 0: + xDropMenu->set_active("hyperlink", true); + break; + case 1: + xDropMenu->set_active("link", true); + break; + case 2: + xDropMenu->set_active("copy", true); + break; + } + + // displayed document + std::unique_ptr xDocMenu(xBuilder->weld_menu("displaymenu")); + sal_uInt16 i=0; + OUString sActive; + OUString sId; + // loaded documents + ScDocShell* pCurrentSh = dynamic_cast( SfxObjectShell::Current() ); + SfxObjectShell* pSh = SfxObjectShell::GetFirst(); + while ( pSh ) + { + if ( dynamic_cast( 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 ( !bHiddenDoc && aName == aManualDoc ) + sActive = sId; + } + pSh = SfxObjectShell::GetNext( *pSh ); + } + // "active window" + ++i; + sId = "document" + OUString::number(i); + xDocMenu->append_radio(sId, pParentWindow->aStrActiveWin); + if (!bHiddenDoc && aManualDoc.isEmpty()) + sActive = sId; + // hidden document + if ( !aHiddenTitle.isEmpty() ) + { + OUString aEntry = aHiddenTitle + pParentWindow->aStrHidden; + ++i; + sId = "document" + OUString::number(i); + xDocMenu->append_radio(sId, aEntry); + if (bHiddenDoc) + sActive = sId; + } + xDocMenu->set_active(sActive.toUtf8(), true); + + OString 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); + } + } + break; + default: break; + } + + return bDone; +} + +IMPL_LINK(ScContentTree, QueryTooltipHdl, const weld::TreeIter&, rEntry, OUString) +{ + OUString aHelpText; + + std::unique_ptr 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() +{ + if (bHiddenDoc) + return pHiddenDocument; + else + { + ScDocShell* pSh = GetManualOrCurrent(); + if (pSh) + return &pSh->GetDocument(); + + } + return nullptr; +} + +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; + + 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; iGetName( 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 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; + + SfxObjectShell* pShell = pDoc->GetDocumentShell(); + if (!pShell) + return; + + // iterate in flat mode for groups + SdrIterMode eIter = ( nType == ScContentId::DRAWING ) ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups; + + std::vector aNames; + SCTAB nTabCount = pDoc->GetTableCount(); + for (SCTAB nTab=0; nTabGetPage(static_cast(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(); + OSL_ENSURE(pLinkManager, "no LinkManager on document?"); + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + sal_uInt16 nCount = rLinks.size(); + for (sal_uInt16 i=0; i( 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(); + OSL_ENSURE(pLinkManager, "no LinkManager on document?"); + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + sal_uInt16 nCount = rLinks.size(); + for (sal_uInt16 i=0; i( 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, u" " ); + 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 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 xEntry(m_xTreeView->make_iterator(pParent)); + bool bEntry = m_xTreeView->iter_children(*xEntry); + + std::vector 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 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(); + SfxObjectShell* pShell = pDoc->GetDocumentShell(); + if (pDrawLayer && pShell) + { + SCTAB nTabCount = pDoc->GetTableCount(); + for (SCTAB nTab=0; nTabGetPage(static_cast(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 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 pTransferObj = new ScDrawTransferObj( std::move(pDragModel), pSrcShell, aObjDesc ); + + pTransferObj->SetDragSourceObj( *pObject, nTab ); + pTransferObj->SetDragSourceFlags(ScDragSrc::Navigator); + + SC_MOD()->SetDragObject( nullptr, pTransferObj.get() ); + + rtl::Reference 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 pTransferObj = new ScTransferObj( std::move(pClipDoc), aObjDesc ); + + pTransferObj->SetDragSource( pSrcShell, aMark ); + pTransferObj->SetDragSourceFlags( nFlags ); + + SC_MOD()->SetDragObject( pTransferObj.get(), nullptr ); // for internal D&D + + rtl::Reference 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; + + std::unique_ptr pDocLoader; + + ScModule* pScMod = SC_MOD(); + + ScContentId nType; + sal_uLong nChild; + + std::unique_ptr 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; + 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, 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 = 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 ) ) + { + 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 xHelper(m_xTransferObj); + m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK); + + bDisallow = false; + } + } + + return bDisallow; +} + +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() ) + return; + + 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::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 xEntry(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xEntry.get())) + { + std::unique_ptr 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(i)]) + continue; + if ((m_xTreeView->iter_compare(*xEntry, *m_aRootNodes[static_cast(i)]) == 0) || + (xParent && m_xTreeView->iter_compare(*xParent, *m_aRootNodes[static_cast(i)]) == 0)) + { + nNew = static_cast(i); + } + } + } + } + + SetRootType( nNew ); +} + +void ScContentTree::ResetManualDoc() +{ + aManualDoc.clear(); + bHiddenDoc = false; + + ActiveDocChanged(); +} + +bool ScContentTree::ActiveDocChanged() +{ + bool bRefreshed = false; + + if ( !bHiddenDoc && aManualDoc.isEmpty() ) + { + Refresh(); // content only if automatic + bRefreshed = true; + } + + // 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(); + 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; + 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.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( 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, std::u16string_view rName) +{ + weld::TreeIter* pParent = m_aRootNodes[nRoot].get(); + + if (pParent || !m_xTreeView->iter_has_child(*pParent)) + return; + + std::unique_ptr 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(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 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(i); + bool bExp = m_aRootNodes[nEntry] && m_xTreeView->get_row_expanded(*m_aRootNodes[nEntry]); + pSettings->SetExpanded( nEntry, bExp ); + } + + std::unique_ptr 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: */ diff --git a/sc/source/ui/navipi/navcitem.cxx b/sc/source/ui/navipi/navcitem.cxx new file mode 100644 index 000000000..871fbd445 --- /dev/null +++ b/sc/source/ui/navipi/navcitem.cxx @@ -0,0 +1,99 @@ +/* -*- 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 +#include +#include + +#include +#include +#include +#include + +ScNavigatorControllerItem::ScNavigatorControllerItem( sal_uInt16 nIdP, + ScNavigatorDlg& rDlg, + SfxBindings& rBindings ) + : SfxControllerItem ( nIdP, rBindings ), + rNavigatorDlg ( rDlg ) +{ +} + +void ScNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 /* nSID */, SfxItemState /* eState */, + const SfxPoolItem* pItem ) +{ + switch( GetId() ) + { + case SID_CURRENTCELL: + if ( pItem ) + { + ScViewData* pViewData = rNavigatorDlg.GetViewData(); + const SfxStringItem* pCellPosItem = dynamic_cast( pItem ); + + OSL_ENSURE( pCellPosItem, "SfxStringItem expected!" ); + + if (pCellPosItem && pViewData) + { + const OUString& aAddress( pCellPosItem->GetValue() ); + ScAddress aScAddress; + aScAddress.Parse(aAddress, pViewData->GetDocument()); + + SCCOL nCol = aScAddress.Col()+1; + SCROW nRow = aScAddress.Row()+1; + + rNavigatorDlg.UpdateColumn( &nCol ); + rNavigatorDlg.UpdateRow ( &nRow ); + } + } + break; + + case SID_CURRENTTAB: + if ( pItem ) + { + const SfxUInt16Item* pTabItem = dynamic_cast< const SfxUInt16Item *>( pItem ); + + OSL_ENSURE( pTabItem, "SfxUInt16Item expected!" ); + + // table for Basic is 1-based + if ( pTabItem && pTabItem->GetValue() ) + { + SCTAB nTab = pTabItem->GetValue() - 1; + + rNavigatorDlg.UpdateTable( &nTab ); + rNavigatorDlg.UpdateColumn(); + rNavigatorDlg.UpdateRow(); + } + } + break; + + case SID_CURRENTDOC: + + // nothing is done via SfxHintId::DocChanged + + break; + + case SID_SELECT_SCENARIO: + rNavigatorDlg.m_xWndScenarios->NotifyState(pItem); + break; + + default: + break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/navipi/navipi.cxx b/sc/source/ui/navipi/navipi.cxx new file mode 100644 index 000000000..44ba8fee2 --- /dev/null +++ b/sc/source/ui/navipi/navipi.cxx @@ -0,0 +1,965 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace com::sun::star; + +// maximum values for UI +static SCCOL SCNAV_MAXCOL(const ScSheetLimits& rLimits) { return rLimits.GetMaxColCount(); } +static sal_Int32 SCNAV_COLDIGITS(const ScSheetLimits& rLimits) +{ + return static_cast( floor( log10( static_cast(SCNAV_MAXCOL(rLimits)))) ) + 1; // 1...256...18278 +} +static sal_Int32 SCNAV_COLLETTERS(const ScSheetLimits& rLimits) +{ + return ::ScColToAlpha(SCNAV_MAXCOL(rLimits)).getLength(); // A...IV...ZZZ +} + +static SCROW SCNAV_MAXROW(const ScSheetLimits& rSheetLimits) +{ + return rSheetLimits.GetMaxRowCount(); +} + +void ScNavigatorDlg::ReleaseFocus() +{ + SfxViewShell* pCurSh = SfxViewShell::Current(); + + if ( pCurSh ) + { + vcl::Window* pShellWnd = pCurSh->GetWindow(); + if ( pShellWnd ) + pShellWnd->GrabFocus(); + } +} + +namespace +{ + SCCOL NumToAlpha(const ScSheetLimits& rSheetLimits, SCCOL nColNo, OUString& rStr) + { + if ( nColNo > SCNAV_MAXCOL(rSheetLimits) ) + nColNo = SCNAV_MAXCOL(rSheetLimits); + else if ( nColNo < 1 ) + nColNo = 1; + + ::ScColToAlpha( rStr, nColNo - 1); + + return nColNo; + } + + SCCOL AlphaToNum(const ScDocument& rDoc, OUString& rStr) + { + SCCOL nColumn = 0; + + if ( CharClass::isAsciiAlpha( rStr) ) + { + rStr = rStr.toAsciiUpperCase(); + + if (::AlphaToCol( rDoc, nColumn, rStr)) + ++nColumn; + + if ( (rStr.getLength() > SCNAV_COLLETTERS(rDoc.GetSheetLimits())) || + (nColumn > SCNAV_MAXCOL(rDoc.GetSheetLimits())) ) + { + nColumn = SCNAV_MAXCOL(rDoc.GetSheetLimits()); + NumToAlpha( rDoc.GetSheetLimits(), nColumn, rStr ); + } + } + else + rStr.clear(); + + return nColumn; + } + + SCCOL NumStrToAlpha(const ScSheetLimits& rSheetLimits, OUString& rStr) + { + SCCOL nColumn = 0; + + if ( CharClass::isAsciiNumeric(rStr) ) + nColumn = NumToAlpha( rSheetLimits, static_cast(rStr.toInt32()), rStr ); + else + rStr.clear(); + + return nColumn; + } +} + +IMPL_LINK(ScNavigatorDlg, ParseRowInputHdl, int*, result, bool) +{ + SCCOL nCol; + + OUString aStrCol = m_xEdCol->get_text(); + + if (!aStrCol.isEmpty()) + { + // nKeyGroup is no longer set at VCL, in cause of lack of keyinput + + ScTabViewShell* pViewSh = dynamic_cast( SfxViewShell::Current() ); + auto& rDoc = pViewSh->GetViewData().GetDocument(); + if ( CharClass::isAsciiNumeric(aStrCol) ) + nCol = NumStrToAlpha( rDoc.GetSheetLimits(), aStrCol ); + else + nCol = AlphaToNum( rDoc, aStrCol ); + } + else + nCol = 0; + + *result = nCol; + return true; +} + +IMPL_LINK_NOARG(ScNavigatorDlg, ExecuteColHdl, weld::Entry&, bool) +{ + ReleaseFocus(); + + SCROW nRow = m_xEdRow->get_value(); + SCCOL nCol = m_xEdCol->get_value(); + + if ( (nCol > 0) && (nRow > 0) ) + SetCurrentCell(nCol - 1, nRow - 1); + + return true; +} + +IMPL_LINK_NOARG(ScNavigatorDlg, FormatRowOutputHdl, weld::SpinButton&, void) +{ + OUString aStr; + ::ScColToAlpha(aStr, m_xEdCol->get_value() - 1); + m_xEdCol->set_text(aStr); +} + +IMPL_LINK_NOARG(ScNavigatorDlg, ExecuteRowHdl, weld::Entry&, bool) +{ + ReleaseFocus(); + + SCCOL nCol = m_xEdCol->get_value(); + SCROW nRow = m_xEdRow->get_value(); + + if ( (nCol > 0) && (nRow > 0) ) + SetCurrentCell(nCol - 1, nRow - 1); + + return true; +} + +IMPL_LINK(ScNavigatorDlg, DocumentSelectHdl, weld::ComboBox&, rListBox, void) +{ + ScNavigatorDlg::ReleaseFocus(); + + OUString aDocName = rListBox.get_active_text(); + m_xLbEntries->SelectDoc(aDocName); +} + +IMPL_LINK(ScNavigatorDlg, ToolBoxSelectHdl, const OString&, rSelId, void) +{ + // Switch the mode? + if (rSelId == "contents" || rSelId == "scenarios") + { + NavListMode eOldMode = eListMode; + NavListMode eNewMode; + + if (rSelId == "scenarios") + { + if (eOldMode == NAV_LMODE_SCENARIOS) + eNewMode = NAV_LMODE_AREAS; + else + eNewMode = NAV_LMODE_SCENARIOS; + } + else // on/off + { + if (eOldMode == NAV_LMODE_NONE) + eNewMode = NAV_LMODE_AREAS; + else + eNewMode = NAV_LMODE_NONE; + } + SetListMode(eNewMode); + UpdateButtons(); + } + else if (rSelId == "dragmode") + m_xTbxCmd2->set_menu_item_active("dragmode", !m_xTbxCmd2->get_menu_item_active("dragmode")); + else + { + if (rSelId == "datarange") + MarkDataArea(); + else if (rSelId == "start") + StartOfDataArea(); + else if (rSelId == "end") + EndOfDataArea(); + else if (rSelId == "toggle") + { + m_xLbEntries->ToggleRoot(); + UpdateButtons(); + } + } +} + +IMPL_LINK(ScNavigatorDlg, ToolBoxDropdownClickHdl, const OString&, rCommand, void) +{ + if (!m_xTbxCmd2->get_menu_item_active(rCommand)) + return; + + // the popup menu of the drop mode has to be called in the + // click (button down) and not in the select (button up) + if (rCommand != "dragmode") + return; + + switch (GetDropMode()) + { + case 0: + m_xDragModeMenu->set_active("hyperlink", true); + break; + case 1: + m_xDragModeMenu->set_active("link", true); + break; + case 2: + m_xDragModeMenu->set_active("copy", true); + break; + } +} + +IMPL_LINK(ScNavigatorDlg, MenuSelectHdl, const OString&, rIdent, void) +{ + if (rIdent == "hyperlink") + SetDropMode(0); + else if (rIdent == "link") + SetDropMode(1); + else if (rIdent == "copy") + SetDropMode(2); +} + +void ScNavigatorDlg::UpdateButtons() +{ + NavListMode eMode = eListMode; + m_xTbxCmd2->set_item_active("scenarios", eMode == NAV_LMODE_SCENARIOS); + m_xTbxCmd1->set_item_active("contents", eMode != NAV_LMODE_NONE); + + // the toggle button: + if (eMode == NAV_LMODE_SCENARIOS || eMode == NAV_LMODE_NONE) + { + m_xTbxCmd2->set_item_sensitive("toggle", false); + m_xTbxCmd2->set_item_active("toggle", false); + } + else + { + m_xTbxCmd2->set_item_sensitive("toggle", true); + bool bRootSet = m_xLbEntries->GetRootType() != ScContentId::ROOT; + m_xTbxCmd2->set_item_active("toggle", bRootSet); + } + + OUString sImageId; + switch (nDropMode) + { + case SC_DROPMODE_URL: + sImageId = RID_BMP_DROP_URL; + break; + case SC_DROPMODE_LINK: + sImageId = RID_BMP_DROP_LINK; + break; + case SC_DROPMODE_COPY: + sImageId = RID_BMP_DROP_COPY; + break; + } + m_xTbxCmd2->set_item_icon_name("dragmode", sImageId); +} + +ScNavigatorSettings::ScNavigatorSettings() + : mnRootSelected(ScContentId::ROOT) + , mnChildSelected(SC_CONTENT_NOCHILD) +{ + maExpandedVec.fill(false); +} + +class ScNavigatorWin : public SfxNavigator +{ +private: + std::unique_ptr m_xNavigator; +public: + ScNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* pMgr, + vcl::Window* pParent, SfxChildWinInfo* pInfo); + virtual void StateChanged(StateChangedType nStateChange) override; + virtual void dispose() override + { + m_xNavigator.reset(); + SfxNavigator::dispose(); + } + virtual ~ScNavigatorWin() override + { + disposeOnce(); + } +}; + +ScNavigatorWin::ScNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr, + vcl::Window* _pParent, SfxChildWinInfo* pInfo) + : SfxNavigator(_pBindings, _pMgr, _pParent, pInfo) +{ + m_xNavigator = std::make_unique(_pBindings, m_xContainer.get(), this); + SetMinOutputSizePixel(GetOptimalSize()); +} + +ScNavigatorDlg::ScNavigatorDlg(SfxBindings* pB, weld::Widget* pParent, SfxNavigator* pNavigatorDlg) + : PanelLayout(pParent, "NavigatorPanel", "modules/scalc/ui/navigatorpanel.ui") + , rBindings(*pB) + , m_xEdCol(m_xBuilder->weld_spin_button("column")) + , m_xEdRow(m_xBuilder->weld_spin_button("row")) + , m_xTbxCmd1(m_xBuilder->weld_toolbar("toolbox1")) + , m_xTbxCmd2(m_xBuilder->weld_toolbar("toolbox2")) + , m_xLbEntries(new ScContentTree(m_xBuilder->weld_tree_view("contentbox"), this)) + , m_xScenarioBox(m_xBuilder->weld_widget("scenariobox")) + , m_xWndScenarios(new ScScenarioWindow(*m_xBuilder, + ScResId(SCSTR_QHLP_SCEN_LISTBOX), ScResId(SCSTR_QHLP_SCEN_COMMENT))) + , m_xLbDocuments(m_xBuilder->weld_combo_box("documents")) + , m_xDragModeMenu(m_xBuilder->weld_menu("dragmodemenu")) + , m_xNavigatorDlg(pNavigatorDlg) + , aContentIdle("ScNavigatorDlg aContentIdle") + , aStrActiveWin(ScResId(SCSTR_ACTIVEWIN)) + , pViewData(nullptr ) + , eListMode(NAV_LMODE_NONE) + , nDropMode(SC_DROPMODE_URL) + , nCurCol(0) + , nCurRow(0) + , nCurTab(0) +{ + UpdateInitShow(); + + UpdateSheetLimits(); + m_xEdRow->set_width_chars(5); + //max rows is 1,000,000, which is too long for typical use + m_xEdRow->connect_activate(LINK(this, ScNavigatorDlg, ExecuteRowHdl)); + + m_xEdCol->connect_activate(LINK(this, ScNavigatorDlg, ExecuteColHdl)); + m_xEdCol->connect_output(LINK(this, ScNavigatorDlg, FormatRowOutputHdl)); + m_xEdCol->connect_input(LINK(this, ScNavigatorDlg, ParseRowInputHdl)); + + m_xTbxCmd1->connect_clicked(LINK(this, ScNavigatorDlg, ToolBoxSelectHdl)); + m_xTbxCmd2->connect_clicked(LINK(this, ScNavigatorDlg, ToolBoxSelectHdl)); + + m_xTbxCmd2->set_item_menu("dragmode", m_xDragModeMenu.get()); + m_xDragModeMenu->connect_activate(LINK(this, ScNavigatorDlg, MenuSelectHdl)); + m_xTbxCmd2->connect_menu_toggled(LINK(this, ScNavigatorDlg, ToolBoxDropdownClickHdl)); + + ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg(); + nDropMode = rCfg.GetDragMode(); + + m_xLbDocuments->set_size_request(42, -1); // set a nominal width so it takes width of surroundings + m_xLbDocuments->connect_changed(LINK(this, ScNavigatorDlg, DocumentSelectHdl)); + aStrActive = " (" + ScResId(SCSTR_ACTIVE) + ")"; // " (active)" + aStrNotActive = " (" + ScResId(SCSTR_NOTACTIVE) + ")"; // " (not active)" + aStrHidden = " (" + ScResId(SCSTR_HIDDEN) + ")"; // " (hidden)" + + rBindings.ENTERREGISTRATIONS(); + + mvBoundItems[0].reset(new ScNavigatorControllerItem(SID_CURRENTCELL,*this,rBindings)); + mvBoundItems[1].reset(new ScNavigatorControllerItem(SID_CURRENTTAB,*this,rBindings)); + mvBoundItems[2].reset(new ScNavigatorControllerItem(SID_CURRENTDOC,*this,rBindings)); + mvBoundItems[3].reset(new ScNavigatorControllerItem(SID_SELECT_SCENARIO,*this,rBindings)); + + rBindings.LEAVEREGISTRATIONS(); + + StartListening( *(SfxGetpApp()) ); + StartListening( rBindings ); + + // was a category chosen as root? + ScContentId nLastRoot = rCfg.GetRootType(); + if ( nLastRoot != ScContentId::ROOT ) + m_xLbEntries->SetRootType( nLastRoot ); + + GetDocNames(nullptr); + + UpdateButtons(); + + UpdateColumn(); + UpdateRow(); + UpdateTable(nullptr); + m_xLbEntries->hide(); + m_xScenarioBox->hide(); + + aContentIdle.SetInvokeHandler( LINK( this, ScNavigatorDlg, TimeHdl ) ); + aContentIdle.SetPriority( TaskPriority::LOWEST ); + + m_xLbEntries->SetNavigatorDlgFlag(true); + + // if scenario was active, switch on + NavListMode eNavMode = static_cast(rCfg.GetListMode()); + if (eNavMode == NAV_LMODE_SCENARIOS) + m_xTbxCmd2->set_item_active("scenarios", true); + else + eNavMode = NAV_LMODE_AREAS; + SetListMode(eNavMode); +} + +weld::Window* ScNavigatorDlg::GetFrameWeld() const +{ + if (m_xNavigatorDlg) + return m_xNavigatorDlg->GetFrameWeld(); + return PanelLayout::GetFrameWeld(); +} + +void ScNavigatorDlg::UpdateSheetLimits() +{ + if (ScViewData* pData = GetViewData()) + { + ScDocument& rDoc = pData->GetDocument(); + m_xEdRow->set_range(1, SCNAV_MAXROW(rDoc.GetSheetLimits())); + m_xEdCol->set_range(1, SCNAV_MAXCOL(rDoc.GetSheetLimits())); + m_xEdCol->set_width_chars(SCNAV_COLDIGITS(rDoc.GetSheetLimits())); // 1...256...18278 or A...IV...ZZZ + } +} + +void ScNavigatorDlg::UpdateInitShow() +{ + // When the navigator is displayed in the sidebar, or is otherwise + // docked, it has the whole deck to fill. Therefore hide the button that + // hides all controls below the top two rows of buttons. + m_xTbxCmd1->set_item_visible("contents", ParentIsFloatingWindow(m_xNavigatorDlg)); +} + +void ScNavigatorWin::StateChanged(StateChangedType nStateChange) +{ + SfxNavigator::StateChanged(nStateChange); + if (nStateChange == StateChangedType::InitShow) + m_xNavigator->UpdateInitShow(); +} + +ScNavigatorDlg::~ScNavigatorDlg() +{ + aContentIdle.Stop(); + + for (auto & p : mvBoundItems) + p.reset(); + pMarkArea.reset(); + + EndListening( *(SfxGetpApp()) ); + EndListening( rBindings ); + + m_xEdCol.reset(); + m_xEdRow.reset(); + m_xTbxCmd1.reset(); + m_xTbxCmd2.reset(); + m_xDragModeMenu.reset(); + m_xLbEntries.reset(); + m_xWndScenarios.reset(); + m_xScenarioBox.reset(); + m_xLbDocuments.reset(); +} + +void ScNavigatorDlg::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if (const SfxEventHint* pHint = dynamic_cast(&rHint)) + { + if (pHint->GetEventId() == SfxEventHintId::ActivateDoc) + { + UpdateSheetLimits(); + bool bRefreshed = m_xLbEntries->ActiveDocChanged(); + // UpdateAll just possibly calls Refresh (and always + // ContentUpdated) so if ActiveDocChanged already called Refresh + // skip re-calling it + if (bRefreshed) + ContentUpdated(); + else + UpdateAll(); + } + } + else + { + const SfxHintId nHintId = rHint.GetId(); + + if (nHintId == SfxHintId::ScDocNameChanged) + { + m_xLbEntries->ActiveDocChanged(); + } + else if (NAV_LMODE_NONE == eListMode) + { + // Table not any more + } + else + { + switch ( nHintId ) + { + case SfxHintId::ScTablesChanged: + m_xLbEntries->Refresh( ScContentId::TABLE ); + break; + + case SfxHintId::ScDbAreasChanged: + m_xLbEntries->Refresh( ScContentId::DBAREA ); + break; + + case SfxHintId::ScAreasChanged: + m_xLbEntries->Refresh( ScContentId::RANGENAME ); + break; + + case SfxHintId::ScDrawChanged: + m_xLbEntries->Refresh( ScContentId::GRAPHIC ); + m_xLbEntries->Refresh( ScContentId::OLEOBJECT ); + m_xLbEntries->Refresh( ScContentId::DRAWING ); + break; + + case SfxHintId::ScAreaLinksChanged: + m_xLbEntries->Refresh( ScContentId::AREALINK ); + break; + + // SfxHintId::DocChanged not only at document change + + case SfxHintId::ScNavigatorUpdateAll: + UpdateAll(); + break; + + case SfxHintId::ScDataChanged: + case SfxHintId::ScAnyDataChanged: + aContentIdle.Start(); // Do not search notes immediately + break; + case SfxHintId::ScSelectionChanged: + UpdateSelection(); + break; + default: + break; + } + } + } +} + +IMPL_LINK( ScNavigatorDlg, TimeHdl, Timer*, pIdle, void ) +{ + if ( pIdle != &aContentIdle ) + return; + + m_xLbEntries->Refresh( ScContentId::NOTE ); +} + +void ScNavigatorDlg::SetDropMode(sal_uInt16 nNew) +{ + nDropMode = nNew; + UpdateButtons(); + ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg(); + rCfg.SetDragMode(nDropMode); +} + +void ScNavigatorDlg::SetCurrentCell( SCCOL nColNo, SCROW nRowNo ) +{ + if ((nColNo+1 == nCurCol) && (nRowNo+1 == nCurRow)) + return; + + // SID_CURRENTCELL == Item #0 clear cache, so it's possible + // setting the current cell even in combined areas + mvBoundItems[0]->ClearCache(); + + ScAddress aScAddress( nColNo, nRowNo, 0 ); + OUString aAddr(aScAddress.Format(ScRefFlags::ADDR_ABS)); + + bool bUnmark = false; + if ( GetViewData() ) + bUnmark = !pViewData->GetMarkData().IsCellMarked( nColNo, nRowNo ); + + SfxStringItem aPosItem( SID_CURRENTCELL, aAddr ); + SfxBoolItem aUnmarkItem( FN_PARAM_1, bUnmark ); // cancel selection + + rBindings.GetDispatcher()->ExecuteList(SID_CURRENTCELL, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aPosItem, &aUnmarkItem }); +} + +void ScNavigatorDlg::SetCurrentCellStr( const OUString& rName ) +{ + mvBoundItems[0]->ClearCache(); + SfxStringItem aNameItem( SID_CURRENTCELL, rName ); + + rBindings.GetDispatcher()->ExecuteList(SID_CURRENTCELL, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aNameItem }); +} + +void ScNavigatorDlg::SetCurrentTable( SCTAB nTabNo ) +{ + if ( nTabNo != nCurTab ) + { + // Table for basic is base-1 + SfxUInt16Item aTabItem( SID_CURRENTTAB, static_cast(nTabNo) + 1 ); + rBindings.GetDispatcher()->ExecuteList(SID_CURRENTTAB, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aTabItem }); + } +} + +void ScNavigatorDlg::SetCurrentTableStr( std::u16string_view rName ) +{ + if (!GetViewData()) return; + + ScDocument& rDoc = pViewData->GetDocument(); + SCTAB nCount = rDoc.GetTableCount(); + OUString aTabName; + SCTAB nLastSheet = 0; + + for (SCTAB i = 0; iExecuteList( SID_CURRENTOBJECT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aNameItem }); +} + +void ScNavigatorDlg::SetCurrentDoc( const OUString& rDocName ) // activate +{ + SfxStringItem aDocItem( SID_CURRENTDOC, rDocName ); + rBindings.GetDispatcher()->ExecuteList( SID_CURRENTDOC, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aDocItem }); +} + +void ScNavigatorDlg::UpdateSelection() +{ + ScTabViewShell* pViewSh = GetTabViewShell(); + if( !pViewSh ) + return; + + uno::Reference< drawing::XShapes > xShapes = pViewSh->getSelectedXShapes(); + if( !xShapes ) + return; + + uno::Reference< container::XIndexAccess > xIndexAccess( + xShapes, uno::UNO_QUERY_THROW ); + if( xIndexAccess->getCount() > 1 ) + return; + uno::Reference< drawing::XShape > xShape; + if( xIndexAccess->getByIndex(0) >>= xShape ) + { + uno::Reference< container::XNamed > xNamed( xShape, uno::UNO_QUERY_THROW ); + OUString sName = xNamed->getName(); + if (!sName.isEmpty()) + { + m_xLbEntries->SelectEntryByName( ScContentId::DRAWING, sName ); + } + } +} + +ScTabViewShell* ScNavigatorDlg::GetTabViewShell() +{ + return dynamic_cast( SfxViewShell::Current() ); +} + +ScNavigatorSettings* ScNavigatorDlg::GetNavigatorSettings() +{ + // Don't store the settings pointer here, because the settings belong to + // the view, and the view may be closed while the navigator is open (reload). + // If the pointer is cached here again later for performance reasons, it has to + // be forgotten when the view is closed. + + ScTabViewShell* pViewSh = GetTabViewShell(); + return pViewSh ? pViewSh->GetNavigatorSettings() : nullptr; +} + +ScViewData* ScNavigatorDlg::GetViewData() +{ + ScTabViewShell* pViewSh = GetTabViewShell(); + pViewData = pViewSh ? &pViewSh->GetViewData() : nullptr; + return pViewData; +} + +void ScNavigatorDlg::UpdateColumn( const SCCOL* pCol ) +{ + if ( pCol ) + nCurCol = *pCol; + else if ( GetViewData() ) + nCurCol = pViewData->GetCurX() + 1; + + m_xEdCol->set_value(nCurCol); +} + +void ScNavigatorDlg::UpdateRow( const SCROW* pRow ) +{ + if ( pRow ) + nCurRow = *pRow; + else if ( GetViewData() ) + nCurRow = pViewData->GetCurY() + 1; + + m_xEdRow->set_value(nCurRow); +} + +void ScNavigatorDlg::UpdateTable( const SCTAB* pTab ) +{ + if ( pTab ) + nCurTab = *pTab; + else if ( GetViewData() ) + nCurTab = pViewData->GetTabNo(); +} + +void ScNavigatorDlg::UpdateAll() +{ + switch (eListMode) + { + case NAV_LMODE_AREAS: + m_xLbEntries->Refresh(); + break; + case NAV_LMODE_NONE: + //! ??? + break; + default: + break; + } + ContentUpdated(); // not again +} + +void ScNavigatorDlg::ContentUpdated() +{ + aContentIdle.Stop(); +} + +void ScNavigatorDlg::SetListMode(NavListMode eMode) +{ + if (eMode != eListMode) + { + bool bForceParentResize = ParentIsFloatingWindow(m_xNavigatorDlg) && + (eMode == NAV_LMODE_NONE || eListMode == NAV_LMODE_NONE); + SfxNavigator* pNav = bForceParentResize ? m_xNavigatorDlg.get() : nullptr; + if (pNav && eMode == NAV_LMODE_NONE) //save last normal size on minimizing + aExpandedSize = pNav->GetSizePixel(); + + eListMode = eMode; + + switch (eMode) + { + case NAV_LMODE_NONE: + ShowList(false); + break; + case NAV_LMODE_AREAS: + m_xLbEntries->Refresh(); + ShowList(true); + break; + case NAV_LMODE_SCENARIOS: + ShowScenarios(); + break; + } + + UpdateButtons(); + + if (eMode != NAV_LMODE_NONE) + { + ScNavipiCfg& rCfg = SC_MOD()->GetNavipiCfg(); + rCfg.SetListMode( static_cast(eMode) ); + } + + if (pNav) + { + pNav->InvalidateChildSizeCache(); + Size aOptimalSize(pNav->GetOptimalSize()); + Size aNewSize(pNav->GetOutputSizePixel()); + aNewSize.setHeight( eMode == NAV_LMODE_NONE ? aOptimalSize.Height() : aExpandedSize.Height() ); + pNav->SetMinOutputSizePixel(aOptimalSize); + pNav->SetOutputSizePixel(aNewSize); + } + } + + if (pMarkArea) + UnmarkDataArea(); +} + +void ScNavigatorDlg::ShowList(bool bShow) +{ + if (bShow) + { + m_xLbEntries->show(); + m_xLbDocuments->show(); + } + else + { + m_xLbEntries->hide(); + m_xLbDocuments->hide(); + } + m_xScenarioBox->hide(); +} + +void ScNavigatorDlg::ShowScenarios() +{ + rBindings.Invalidate( SID_SELECT_SCENARIO ); + rBindings.Update( SID_SELECT_SCENARIO ); + + m_xScenarioBox->show(); + m_xLbDocuments->show(); + m_xLbEntries->hide(); +} + +// documents for Dropdown-Listbox +void ScNavigatorDlg::GetDocNames( const OUString* pManualSel ) +{ + m_xLbDocuments->clear(); + m_xLbDocuments->freeze(); + + ScDocShell* pCurrentSh = dynamic_cast( SfxObjectShell::Current() ); + + OUString aSelEntry; + SfxObjectShell* pSh = SfxObjectShell::GetFirst(); + while ( pSh ) + { + if ( dynamic_cast( pSh) != nullptr ) + { + OUString aName = pSh->GetTitle(); + OUString aEntry = aName; + if (pSh == pCurrentSh) + aEntry += aStrActive; + else + aEntry += aStrNotActive; + m_xLbDocuments->append_text(aEntry); + + if ( pManualSel ? ( aName == *pManualSel ) + : ( pSh == pCurrentSh ) ) + aSelEntry = aEntry; // complete entry for selection + } + + pSh = SfxObjectShell::GetNext( *pSh ); + } + + m_xLbDocuments->append_text(aStrActiveWin); + + OUString aHidden = m_xLbEntries->GetHiddenTitle(); + if (!aHidden.isEmpty()) + { + OUString aEntry = aHidden + aStrHidden; + m_xLbDocuments->append_text(aEntry); + + if ( pManualSel && aHidden == *pManualSel ) + aSelEntry = aEntry; + } + + m_xLbDocuments->thaw(); + + m_xLbDocuments->set_active_text(aSelEntry); +} + +void ScNavigatorDlg::MarkDataArea() +{ + ScTabViewShell* pViewSh = GetTabViewShell(); + + if ( !pViewSh ) + return; + + if ( !pMarkArea ) + pMarkArea.reset( new ScArea ); + + pViewSh->MarkDataArea(); + const ScRange& aMarkRange = pViewSh->GetViewData().GetMarkData().GetMarkArea(); + pMarkArea->nColStart = aMarkRange.aStart.Col(); + pMarkArea->nRowStart = aMarkRange.aStart.Row(); + pMarkArea->nColEnd = aMarkRange.aEnd.Col(); + pMarkArea->nRowEnd = aMarkRange.aEnd.Row(); + pMarkArea->nTab = aMarkRange.aStart.Tab(); +} + +void ScNavigatorDlg::UnmarkDataArea() +{ + ScTabViewShell* pViewSh = GetTabViewShell(); + + if ( pViewSh ) + { + pViewSh->Unmark(); + pMarkArea.reset(); + } +} + +void ScNavigatorDlg::StartOfDataArea() +{ + // pMarkArea evaluate ??? + + if ( GetViewData() ) + { + ScMarkData& rMark = pViewData->GetMarkData(); + const ScRange& aMarkRange = rMark.GetMarkArea(); + + SCCOL nCol = aMarkRange.aStart.Col(); + SCROW nRow = aMarkRange.aStart.Row(); + + if ( (nCol+1 != m_xEdCol->get_value()) || (nRow+1 != m_xEdRow->get_value()) ) + SetCurrentCell( nCol, nRow ); + } +} + +void ScNavigatorDlg::EndOfDataArea() +{ + // pMarkArea evaluate ??? + + if ( GetViewData() ) + { + ScMarkData& rMark = pViewData->GetMarkData(); + const ScRange& aMarkRange = rMark.GetMarkArea(); + + SCCOL nCol = aMarkRange.aEnd.Col(); + SCROW nRow = aMarkRange.aEnd.Row(); + + if ( (nCol+1 != m_xEdCol->get_value()) || (nRow+1 != m_xEdRow->get_value()) ) + SetCurrentCell( nCol, nRow ); + } +} + +SFX_IMPL_DOCKINGWINDOW(ScNavigatorWrapper, SID_NAVIGATOR); + +ScNavigatorWrapper::ScNavigatorWrapper(vcl::Window *_pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo) + : SfxNavigatorWrapper(_pParent, nId) +{ + SetWindow(VclPtr::Create(pBindings, this, _pParent, pInfo)); + Initialize(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/navipi/scenwnd.cxx b/sc/source/ui/navipi/scenwnd.cxx new file mode 100644 index 000000000..3bbc6309c --- /dev/null +++ b/sc/source/ui/navipi/scenwnd.cxx @@ -0,0 +1,242 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// class ScScenarioWindow ------------------------------------------------ + +void ScScenarioWindow::UpdateEntries( const std::vector &rNewEntryList ) +{ + m_xLbScenario->clear(); + m_aEntries.clear(); + + switch( rNewEntryList.size() ) + { + case 0: + // no scenarios in current sheet + SetComment( OUString() ); + break; + + case 1: + // sheet is a scenario container, comment only + SetComment( rNewEntryList[0] ); + break; + + default: + { + // sheet contains scenarios + assert(rNewEntryList.size() % 3 == 0 && "ScScenarioListBox::UpdateEntries - wrong list size"); + m_xLbScenario->freeze(); + + std::vector::const_iterator iter; + for (iter = rNewEntryList.begin(); iter != rNewEntryList.end(); ++iter) + { + ScenarioEntry aEntry; + + // first entry of a triple is the scenario name + aEntry.maName = *iter; + + // second entry of a triple is the scenario comment + ++iter; + aEntry.maComment = *iter; + + // third entry of a triple is the protection ("0" = not protected, "1" = protected) + ++iter; + aEntry.mbProtected = !(*iter).isEmpty() && (*iter)[0] != '0'; + + m_aEntries.push_back( aEntry ); + m_xLbScenario->append_text(aEntry.maName); + } + m_xLbScenario->thaw(); + m_xLbScenario->unselect_all(); + SetComment(OUString()); + } + } +} + +IMPL_LINK_NOARG(ScScenarioWindow, SelectHdl, weld::TreeView&, void) +{ + if (const ScenarioEntry* pEntry = GetSelectedScenarioEntry()) + SetComment(pEntry->maComment); +} + +IMPL_LINK_NOARG(ScScenarioWindow, DoubleClickHdl, weld::TreeView&, bool) +{ + SelectScenario(); + return true; +} + +IMPL_LINK(ScScenarioWindow, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + switch( aCode.GetCode() ) + { + case KEY_RETURN: + SelectScenario(); + bHandled = true; + break; + case KEY_DELETE: + DeleteScenario(); + bHandled = true; + break; + } + + return bHandled; +} + +IMPL_LINK(ScScenarioWindow, ContextMenuHdl, const CommandEvent&, rCEvt, bool) +{ + bool bHandled = false; + + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + if (const ScenarioEntry* pEntry = GetSelectedScenarioEntry()) + { + if (!pEntry->mbProtected) + { + std::unique_ptr xBuilder(Application::CreateBuilder(m_xLbScenario.get(), "modules/scalc/ui/scenariomenu.ui")); + std::unique_ptr xPopup(xBuilder->weld_menu("menu")); + OString sIdent(xPopup->popup_at_rect(m_xLbScenario.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); + if (sIdent == "delete") + DeleteScenario(); + else if (sIdent == "edit") + EditScenario(); + } + } + bHandled = true; + } + + return bHandled; +} + +const ScScenarioWindow::ScenarioEntry* ScScenarioWindow::GetSelectedScenarioEntry() const +{ + size_t nPos = m_xLbScenario->get_selected_index(); + return (nPos < m_aEntries.size()) ? &m_aEntries[ nPos ] : nullptr; +} + +void ScScenarioWindow::ExecuteScenarioSlot(sal_uInt16 nSlotId) +{ + if( SfxViewFrame* pViewFrm = SfxViewFrame::Current() ) + { + SfxStringItem aStringItem(nSlotId, m_xLbScenario->get_selected_text()); + pViewFrm->GetDispatcher()->ExecuteList(nSlotId, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aStringItem } ); + } +} + +void ScScenarioWindow::SelectScenario() +{ + if (m_xLbScenario->get_selected_index() != -1) + ExecuteScenarioSlot(SID_SELECT_SCENARIO); +} + +void ScScenarioWindow::EditScenario() +{ + if (m_xLbScenario->get_selected_index() != -1) + ExecuteScenarioSlot(SID_EDIT_SCENARIO); +} + +void ScScenarioWindow::DeleteScenario() +{ + if (m_xLbScenario->get_selected_index() != -1) + { + std::unique_ptr xQueryBox(Application::CreateMessageDialog(m_xLbScenario.get(), + VclMessageType::Question, VclButtonsType::YesNo, + ScResId(STR_QUERY_DELSCENARIO))); + xQueryBox->set_default_response(RET_YES); + if (xQueryBox->run() == RET_YES) + ExecuteScenarioSlot(SID_DELETE_SCENARIO); + } +} + +// class ScScenarioWindow ------------------------------------------------ + +ScScenarioWindow::ScScenarioWindow(weld::Builder& rBuilder, const OUString& aQH_List, + const OUString& aQH_Comment) + : m_xLbScenario(rBuilder.weld_tree_view("scenariolist")) + , m_xEdComment(rBuilder.weld_text_view("scenariotext")) +{ + m_xLbScenario->set_help_id(HID_SC_SCENWIN_TOP); + m_xEdComment->set_help_id(HID_SC_SCENWIN_BOTTOM); + + m_xLbScenario->set_tooltip_text(aQH_List); + m_xEdComment->set_tooltip_text(aQH_Comment); + + m_xLbScenario->connect_changed(LINK(this, ScScenarioWindow, SelectHdl)); + m_xLbScenario->connect_row_activated(LINK(this, ScScenarioWindow, DoubleClickHdl)); + m_xLbScenario->connect_key_press(LINK(this, ScScenarioWindow, KeyInputHdl)); + m_xLbScenario->connect_popup_menu(LINK(this, ScScenarioWindow, ContextMenuHdl)); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + SfxBindings& rBindings = pViewFrm->GetBindings(); + rBindings.Invalidate( SID_SELECT_SCENARIO ); + rBindings.Update( SID_SELECT_SCENARIO ); + } +} + +ScScenarioWindow::~ScScenarioWindow() +{ +} + +void ScScenarioWindow::NotifyState( const SfxPoolItem* pState ) +{ + if( pState ) + { + m_xLbScenario->set_sensitive(true); + + if ( auto pStringItem = dynamic_cast( pState) ) + { + const OUString& aNewEntry( pStringItem->GetValue() ); + + if (!aNewEntry.isEmpty()) + m_xLbScenario->select_text(aNewEntry); + else + m_xLbScenario->unselect_all(); + } + else if ( auto pStringListItem = dynamic_cast( pState) ) + { + UpdateEntries(pStringListItem->GetList()); + } + } + else + { + m_xLbScenario->set_sensitive(false); + m_xLbScenario->unselect_all(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3