tdf#129610 SdNavigator: Realize multiple selection of objects
This enhancement patch makes possible, mutiple selection of objects
in the draw view using the Navigator page/objects tree. Standard
multi-select mouse and keyboard methods, Ctrl/Shift + mouse button/
keyboard navigation keys, seem to work as expected with gtk3,
x11, and qt vcl backends. Double mouse click and the Enter key, moves
keyboard focus to the selected objects selection frame. Single mouse
click and keyboard navigation keys select the object in the view
while keeping the keyboard focus in the Navigator tree. Patch code
done to make navigation to empty name objects, commit
f0878173e1963cf8db5f60ced6d19da24e18bc41, is moved to the
SdNavigatorWin selection object handler, ClickObjectHdl, by this
patch.
UITesting:
TreeListEntryUIObject "DOUBLECLICK" action change:
Multiple selection as implemented in this patch, requires the tree
list entry to become the cursor entry when selected.
Change-Id: I58a36117242e40ce2811488c1fa8ef2aa72b28e5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141440
Tested-by: Jenkins
Reviewed-by: Jim Raykowski <raykowj@gmail.com>
diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx
index 6aca8fd..162b301 100644
--- a/sd/source/ui/dlg/navigatr.cxx
+++ b/sd/source/ui/dlg/navigatr.cxx
@@ -67,6 +67,7 @@ SdNavigatorWin::SdNavigatorWin(weld::Widget* pParent, SfxBindings* pInBindings,
mxTlbObjects->connect_row_activated(LINK(this, SdNavigatorWin, ClickObjectHdl));
mxTlbObjects->set_selection_mode(SelectionMode::Multiple);
mxTlbObjects->connect_mouse_release(LINK(this, SdNavigatorWin, MouseReleaseHdl));
mxToolbox->connect_clicked(LINK(this, SdNavigatorWin, SelectToolboxHdl));
mxToolbox->connect_menu_toggled(LINK(this, SdNavigatorWin, DropdownClickToolBoxHdl));
@@ -232,6 +233,11 @@ SdPageObjsTLV& SdNavigatorWin::GetObjects()
return *mxTlbObjects;
}
IMPL_STATIC_LINK_NOARG(SdNavigatorWin, MouseReleaseHdl, const MouseEvent&, bool)
{
return true;
}
IMPL_LINK(SdNavigatorWin, SelectToolboxHdl, const OString&, rCommand, void)
{
PageJump ePage = PAGE_NONE;
@@ -291,14 +297,80 @@ IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool)
// if it is the active window, we jump to the page
if( pInfo && pInfo->IsActive() )
{
OUString aStr(mxTlbObjects->get_selected_text());
OUString aStr(mxTlbObjects->get_cursor_text());
if( !aStr.isEmpty() )
{
SfxStringItem aItem( SID_NAVIGATOR_OBJECT, aStr );
mpBindings->GetDispatcher()->ExecuteList(
SID_NAVIGATOR_OBJECT,
SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });
sd::DrawDocShell* pDocShell = pInfo->mpDocShell;
if (!pDocShell)
return false;
sd::ViewShell* pViewShell = pDocShell->GetViewShell();
if (!pViewShell)
return false;
SdrView* pDrawView = pViewShell->GetDrawView();
if (!pDrawView)
return false;
// Save the selected tree entries re-mark the objects in the view after navigation.
auto vSelectedEntryIds = mxTlbObjects->GetSelectedEntryIds();
// Page entries in the tree have id value 1. Object entries have id value of
// the address of the pointer to the object.
const auto& rCursorEntryId = mxTlbObjects->get_cursor_id();
auto nCursorEntryId = rCursorEntryId.toInt64();
SdrObject* pCursorEntryObject = weld::fromId<SdrObject*>(rCursorEntryId);
bool bIsCursorEntrySelected(std::find(vSelectedEntryIds.begin(),
vSelectedEntryIds.end(),
rCursorEntryId) != vSelectedEntryIds.end());
if (bIsCursorEntrySelected)
{
// Set a temporary name, if need be, so the object can be navigated to.
bool bCursorEntryObjectHasEmptyName = false;
if (nCursorEntryId != 1 && pCursorEntryObject
&& pCursorEntryObject->GetName().isEmpty())
{
bCursorEntryObjectHasEmptyName = true;
bool bUndo = pCursorEntryObject->getSdrModelFromSdrObject().IsUndoEnabled();
pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(false);
pCursorEntryObject->SetName(aStr, false);
pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
}
// All objects are unmarked when navigating to an object.
SfxStringItem aItem(SID_NAVIGATOR_OBJECT, aStr);
mpBindings->GetDispatcher()->ExecuteList(SID_NAVIGATOR_OBJECT,
SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem });
if (bCursorEntryObjectHasEmptyName)
{
bool bUndo = pCursorEntryObject->getSdrModelFromSdrObject().IsUndoEnabled();
pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(false);
pCursorEntryObject->SetName(OUString(), false);
pCursorEntryObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
}
// re-mark the objects
if (bIsCursorEntrySelected)
{
// Mark the objects in the view that are selected in the Navigator tree.
for (auto& rEntryId: vSelectedEntryIds)
{
if (rEntryId != "1")
{
SdrObject* pEntryObject = weld::fromId<SdrObject*>(rEntryId);
if (pEntryObject)
pDrawView->MarkObj(pEntryObject, pDrawView->GetSdrPageView());
}
}
}
}
else if (nCursorEntryId != 1 && pCursorEntryObject)
{
// unmark
pDrawView->MarkObj(pCursorEntryObject, pDrawView->GetSdrPageView(), true);
}
// moved here from SetGetFocusHdl. Reset the
// focus only if something has been selected in the
@@ -316,17 +388,9 @@ IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool)
// still the slide sorter. Explicitly try to grab the draw
// shell focus, so follow-up operations work with the object
// and not with the whole slide.
sd::DrawDocShell* pDocShell = pInfo->mpDocShell;
if (pDocShell)
{
sd::ViewShell* pViewShell = pDocShell->GetViewShell();
if (pViewShell)
{
vcl::Window* pWindow = pViewShell->GetActiveWindow();
if (pWindow)
pWindow->GrabFocus();
}
}
vcl::Window* pWindow = pViewShell->GetActiveWindow();
if (pWindow)
pWindow->GrabFocus();
if (!mxTlbObjects->IsNavigationGrabsFocus())
// This is the case when keyboard navigation inside the
diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx
index 3e86c4b..d63b1cf 100644
--- a/sd/source/ui/dlg/sdtreelb.cxx
+++ b/sd/source/ui/dlg/sdtreelb.cxx
@@ -304,7 +304,9 @@ IMPL_LINK(SdPageObjsTLV, KeyInputHdl, const KeyEvent&, rKEvt, bool)
else
m_xTreeView->expand_row(*xCursor);
}
m_bNavigationGrabsFocus = true;
m_aRowActivatedHdl.Call(*m_xTreeView);
m_bNavigationGrabsFocus = false;
return true;
}
return m_aKeyPressHdl.Call(rKEvt);
@@ -319,6 +321,9 @@ IMPL_LINK(SdPageObjsTLV, MousePressHdl, const MouseEvent&, rMEvt, bool)
IMPL_LINK_NOARG(SdPageObjsTLV, MouseReleaseHdl, const MouseEvent&, bool)
{
if (m_aMouseReleaseHdl.IsSet() && m_aMouseReleaseHdl.Call(MouseEvent()))
return false;
m_bSelectionHandlerNavigates = false;
m_bNavigationGrabsFocus = true;
return false;
@@ -666,7 +671,7 @@ IMPL_LINK_NOARG(SdPageObjsTLV, RowActivatedHdl, weld::TreeView&, bool)
Application::RemoveUserEvent(m_nRowActivateEventId);
// post the event to process row activate after mouse press event
m_nRowActivateEventId = Application::PostUserEvent(LINK(this, SdPageObjsTLV, AsyncRowActivatedHdl));
return true;
return false;
}
IMPL_LINK_NOARG(SdPageObjsTLV, AsyncSelectHdl, void*, void)
@@ -689,30 +694,7 @@ void SdPageObjsTLV::Select()
m_aChangeHdl.Call(*m_xTreeView);
if (m_bSelectionHandlerNavigates)
{
// Page items in the tree are given user data value 1.
// Drawing object items are given user data value of the object pointer they represent.
sal_Int64 nUserData = m_xTreeView->get_selected_id().toInt64();
if (nUserData != 1)
{
SdrObject* pObject = reinterpret_cast<SdrObject*>(nUserData);
if (pObject && pObject->GetName().isEmpty())
{
const bool bUndo = pObject->getSdrModelFromSdrObject().IsUndoEnabled();
pObject->getSdrModelFromSdrObject().EnableUndo(false);
pObject->SetName(m_xTreeView->get_selected_text(), false);
pObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
m_aRowActivatedHdl.Call(*m_xTreeView);
pObject->getSdrModelFromSdrObject().EnableUndo(false);
pObject->SetName(OUString(), false);
pObject->getSdrModelFromSdrObject().EnableUndo(bUndo);
}
else
m_aRowActivatedHdl.Call(*m_xTreeView);
}
else
m_aRowActivatedHdl.Call(*m_xTreeView);
}
m_aRowActivatedHdl.Call(*m_xTreeView);
if (!m_pNavigator)
{
@@ -724,7 +706,7 @@ void SdPageObjsTLV::Select()
OUString aURL = INetURLObject(pDocShell->GetMedium()->GetPhysicalName(), INetProtocol::File).GetMainURL(INetURLObject::DecodeMechanism::NONE);
NavigatorDragType eDragType = m_pNavigator->GetNavigatorDragType();
OUString sSelectedEntry = m_xTreeView->get_selected_text();
OUString sSelectedEntry = get_cursor_text(); // what about multiple selections?
aURL += "#" + sSelectedEntry;
INetBookmark aBookmark(aURL, sSelectedEntry);
@@ -791,6 +773,18 @@ std::vector<OUString> SdPageObjsTLV::GetSelectEntryList(const int nDepth) const
return aEntries;
}
std::vector<OUString> SdPageObjsTLV::GetSelectedEntryIds() const
{
std::vector<OUString> vEntryIds;
m_xTreeView->selected_foreach([this, &vEntryIds](weld::TreeIter& rEntry){
vEntryIds.push_back(m_xTreeView->get_id(rEntry));
return false;
});
return vEntryIds;
}
/**
* Checks if it is a draw file and opens the BookmarkDoc depending of
* the provided Docs
diff --git a/sd/source/ui/inc/navigatr.hxx b/sd/source/ui/inc/navigatr.hxx
index 7eeb255..f0b7d1f 100644
--- a/sd/source/ui/inc/navigatr.hxx
+++ b/sd/source/ui/inc/navigatr.hxx
@@ -158,6 +158,7 @@ private:
DECL_DLLPRIVATE_LINK( MenuSelectHdl, const OString&, void );
DECL_DLLPRIVATE_LINK( ShapeFilterCallback, const OString&, void );
DECL_DLLPRIVATE_LINK( KeyInputHdl, const KeyEvent&, bool );
DECL_DLLPRIVATE_STATIC_LINK(SdNavigatorWin, MouseReleaseHdl, const MouseEvent&, bool);
void SetDragImage();
diff --git a/sd/source/ui/inc/sdtreelb.hxx b/sd/source/ui/inc/sdtreelb.hxx
index 84432f1..38a7975 100644
--- a/sd/source/ui/inc/sdtreelb.hxx
+++ b/sd/source/ui/inc/sdtreelb.hxx
@@ -105,6 +105,7 @@ private:
Link<weld::TreeView&, void> m_aChangeHdl;
Link<weld::TreeView&, bool> m_aRowActivatedHdl;
Link<const KeyEvent&, bool> m_aKeyPressHdl;
Link<const MouseEvent&, bool> m_aMouseReleaseHdl;
/** Return the name of the object. When the object has no user supplied
name and the bCreate flag is <TRUE/> then a name is created
@@ -216,6 +217,11 @@ public:
m_aKeyPressHdl = rLink;
}
void connect_mouse_release(const Link<const MouseEvent&, bool>& rLink)
{
m_aMouseReleaseHdl = rLink;
}
bool HasSelectedChildren(std::u16string_view rName);
bool SelectEntry(std::u16string_view rName);
void SelectEntry(const SdrObject* pObj);
@@ -265,6 +271,22 @@ public:
m_xTreeView->unselect_all();
}
OUString get_cursor_text() const
{
std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
if (m_xTreeView->get_cursor(xIter.get()))
return m_xTreeView->get_text(*xIter);
return OUString();
}
OUString get_cursor_id() const
{
std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
if (m_xTreeView->get_cursor(xIter.get()))
return m_xTreeView->get_id(*xIter);
return OUString();
}
void SetViewFrame(const SfxViewFrame* pViewFrame);
void Fill(const SdDrawDocument*, bool bAllPages, const OUString& rDocName);
@@ -324,6 +346,8 @@ public:
std::vector<OUString> GetSelectEntryList(const int nDepth) const;
std::vector<OUString> GetSelectedEntryIds() const;
SdDrawDocument* GetBookmarkDoc(SfxMedium* pMedium = nullptr);
bool IsLinkableSelected() const { return m_bLinkableSelected; }
diff --git a/vcl/source/treelist/uiobject.cxx b/vcl/source/treelist/uiobject.cxx
index 89d9280..4ab051a 100644
--- a/vcl/source/treelist/uiobject.cxx
+++ b/vcl/source/treelist/uiobject.cxx
@@ -157,7 +157,7 @@ void TreeListEntryUIObject::execute(const OUString& rAction, const StringMap& /*
}
else if (rAction == "DOUBLECLICK")
{
mxTreeList->Select(mpEntry);
mxTreeList->SetCurEntry(mpEntry);
mxTreeList->DoubleClickHdl();
}
}