summaryrefslogtreecommitdiffstats
path: root/sc/source/ui/navipi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sc/source/ui/navipi
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/ui/navipi')
-rw-r--r--sc/source/ui/navipi/content.cxx1548
-rw-r--r--sc/source/ui/navipi/navcitem.cxx99
-rw-r--r--sc/source/ui/navipi/navipi.cxx965
-rw-r--r--sc/source/ui/navipi/scenwnd.cxx243
4 files changed, 2855 insertions, 0 deletions
diff --git a/sc/source/ui/navipi/content.cxx b/sc/source/ui/navipi/content.cxx
new file mode 100644
index 0000000000..89d7764255
--- /dev/null
+++ b/sc/source/ui/navipi/content.cxx
@@ -0,0 +1,1548 @@
+/* -*- 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 <scmod.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <tablink.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(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() );
+ }
+ 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;
+
+ switch ( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // drag-and-drop mode
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/scalc/ui/dropmenu.ui"));
+ std::unique_ptr<weld::Menu> xPop(xBuilder->weld_menu("contextmenu"));
+ std::unique_ptr<weld::Menu> 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<weld::Menu> xDocMenu(xBuilder->weld_menu("displaymenu"));
+ 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);
+
+ 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);
+ }
+ }
+ 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();
+ OSL_ENSURE(pLinkManager, "no LinkManager on document?");
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto 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();
+ OSL_ENSURE(pLinkManager, "no LinkManager on document?");
+ const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks();
+ sal_uInt16 nCount = rLinks.size();
+ for (sal_uInt16 i=0; i<nCount; i++)
+ {
+ ::sfx2::SvBaseLink* pBase = rLinks[i].get();
+ if (auto pAreaLink = dynamic_cast<const ScAreaLink*>( pBase))
+ {
+ if (nFound == nIndex)
+ return pAreaLink;
+ ++nFound;
+ }
+ }
+
+ OSL_FAIL("link not found");
+ return nullptr;
+}
+
+static OUString lcl_NoteString( const ScPostIt& rNote )
+{
+ 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: */
diff --git a/sc/source/ui/navipi/navcitem.cxx b/sc/source/ui/navipi/navcitem.cxx
new file mode 100644
index 0000000000..871fbd445e
--- /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 <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <osl/diagnose.h>
+
+#include <navcitem.hxx>
+#include <navipi.hxx>
+#include <viewdata.hxx>
+#include <sc.hrc>
+
+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<const SfxStringItem*>( 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 0000000000..5fc6fee821
--- /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 <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/navigat.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/charclass.hxx>
+
+#include <viewdata.hxx>
+#include <tabvwsh.hxx>
+#include <docsh.hxx>
+#include <document.hxx>
+#include <rangeutl.hxx>
+#include <sc.hrc>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <scresid.hxx>
+#include <scmod.hxx>
+#include <navicfg.hxx>
+#include <navcitem.hxx>
+#include <navipi.hxx>
+#include <navsett.hxx>
+#include <markdata.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+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<sal_Int32>( floor( log10( static_cast<double>(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<SCCOL>(rStr.toInt32()), rStr );
+ else
+ rStr.clear();
+
+ return nColumn;
+ }
+}
+
+IMPL_LINK(ScNavigatorDlg, ParseRowInputHdl, int*, result, bool)
+{
+ SCCOL nCol(0);
+
+ OUString aStrCol = m_xEdCol->get_text();
+
+ if (!aStrCol.isEmpty())
+ {
+ if (ScViewData* pData = GetViewData())
+ {
+ ScDocument& rDoc = pData->GetDocument();
+
+ if ( CharClass::isAsciiNumeric(aStrCol) )
+ nCol = NumStrToAlpha( rDoc.GetSheetLimits(), aStrCol );
+ else
+ nCol = AlphaToNum( rDoc, aStrCol );
+ }
+ }
+
+ *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 OUString&, 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 OUString&, 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 OUString&, rIdent, void)
+{
+ if (rIdent == u"hyperlink")
+ SetDropMode(0);
+ else if (rIdent == u"link")
+ SetDropMode(1);
+ else if (rIdent == u"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<ScNavigatorDlg> 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<ScNavigatorDlg>(_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)"
+
+ 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<NavListMode>(rCfg.GetListMode());
+ if (eNavMode == NAV_LMODE_SCENARIOS)
+ m_xTbxCmd2->set_item_active("scenarios", true);
+ else
+ eNavMode = NAV_LMODE_AREAS;
+ SetListMode(eNavMode);
+
+ if(comphelper::LibreOfficeKit::isActive())
+ {
+ m_xBuilder->weld_container("gridbuttons")->hide();
+ m_xLbDocuments->hide();
+ }
+}
+
+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();
+ moMarkArea.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 (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
+ {
+ // This is for when the document might change and the navigator
+ // wants to update for the new document, which isn't a scenario
+ // that happens in online.
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ const SfxEventHint& rEventHint = static_cast<const SfxEventHint&>(rHint);
+ if (rEventHint.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<sal_uInt16>(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; i<nCount; i++)
+ {
+ rDoc.GetName(i, aTabName);
+ if (aTabName == rName)
+ {
+ // Check if this is a Scenario sheet and if so select the sheet
+ // where it belongs to, which is the previous non-Scenario sheet.
+ if (rDoc.IsScenario(i))
+ {
+ SetCurrentTable(nLastSheet);
+ return;
+ }
+ else
+ {
+ SetCurrentTable(i);
+ return;
+ }
+ }
+ else
+ {
+ if (!rDoc.IsScenario(i))
+ nLastSheet = i;
+ }
+ }
+}
+
+void ScNavigatorDlg::SetCurrentObject( const OUString& rName )
+{
+ SfxStringItem aNameItem( SID_CURRENTOBJECT, rName );
+ rBindings.GetDispatcher()->ExecuteList( 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<ScTabViewShell*>( 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<sal_uInt16>(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 (moMarkArea)
+ 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<ScDocShell*>( SfxObjectShell::Current() );
+
+ OUString aSelEntry;
+ SfxObjectShell* pSh = SfxObjectShell::GetFirst();
+ while ( pSh )
+ {
+ if ( dynamic_cast<const ScDocShell*>( 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);
+
+ m_xLbDocuments->thaw();
+
+ m_xLbDocuments->set_active_text(aSelEntry);
+}
+
+void ScNavigatorDlg::MarkDataArea()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+
+ if ( !pViewSh )
+ return;
+
+ if ( !moMarkArea )
+ moMarkArea.emplace();
+
+ pViewSh->MarkDataArea();
+ const ScRange& aMarkRange = pViewSh->GetViewData().GetMarkData().GetMarkArea();
+ moMarkArea->nColStart = aMarkRange.aStart.Col();
+ moMarkArea->nRowStart = aMarkRange.aStart.Row();
+ moMarkArea->nColEnd = aMarkRange.aEnd.Col();
+ moMarkArea->nRowEnd = aMarkRange.aEnd.Row();
+ moMarkArea->nTab = aMarkRange.aStart.Tab();
+}
+
+void ScNavigatorDlg::UnmarkDataArea()
+{
+ ScTabViewShell* pViewSh = GetTabViewShell();
+
+ if ( pViewSh )
+ {
+ pViewSh->Unmark();
+ moMarkArea.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<ScNavigatorWin>::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 0000000000..22ee5148fc
--- /dev/null
+++ b/sc/source/ui/navipi/scenwnd.cxx
@@ -0,0 +1,243 @@
+/* -*- 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 <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <navipi.hxx>
+#include <sc.hrc>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <helpids.h>
+
+// class ScScenarioWindow ------------------------------------------------
+
+void ScScenarioWindow::UpdateEntries( const std::vector<OUString> &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<OUString>::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<weld::Builder> xBuilder(Application::CreateBuilder(m_xLbScenario.get(), "modules/scalc/ui/scenariomenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+ OUString 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<weld::MessageDialog> 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<const SfxStringItem*>( 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<const SfxStringListItem*>( pState) )
+ {
+ UpdateEntries(pStringListItem->GetList());
+ }
+ }
+ else
+ {
+ m_xLbScenario->set_sensitive(false);
+ m_xLbScenario->unselect_all();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */