sd/source/ui/dlg/navigatr.cxx    |   96 ++++++++++++++++++++++++++++++++-------
 sd/source/ui/dlg/sdtreelb.cxx    |   46 ++++++++----------
 sd/source/ui/inc/navigatr.hxx    |    1 
 sd/source/ui/inc/sdtreelb.hxx    |   24 +++++++++
 vcl/source/treelist/uiobject.cxx |    2 
 5 files changed, 126 insertions(+), 43 deletions(-)

New commits:
commit 060c529c9c450ddc22912ee88af5305c1fd063f3
Author:     Jim Raykowski <[email protected]>
AuthorDate: Sun Oct 16 22:07:32 2022 -0800
Commit:     Jim Raykowski <[email protected]>
CommitDate: Sun Oct 30 23:52:35 2022 +0100

    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 <[email protected]>

diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx
index 6aca8fd0f2fb..162b3011a23d 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 3e86c4bc2376..d63b1cf13645 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 7eeb2551a778..f0b7d1fca5e8 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 84432f180834..38a797519654 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 89d92808c3fb..4ab051a868e1 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();
     }
 }

Reply via email to