sd/UIConfig_sdraw.mk | 1 sd/source/ui/dlg/navigatr.cxx | 30 ++++++- sd/source/ui/dlg/sdtreelb.cxx | 114 ++++++++++++++++++++++++++- sd/source/ui/inc/navigatr.hxx | 3 sd/source/ui/inc/sdtreelb.hxx | 31 +++++++ sd/uiconfig/sdraw/ui/navigatorcontextmenu.ui | 17 ++++ 6 files changed, 194 insertions(+), 2 deletions(-)
New commits: commit ace75043781b5fe36546ec75574a14617f4feb30 Author: Jim Raykowski <rayk...@gmail.com> AuthorDate: Mon Oct 24 23:32:30 2022 -0800 Commit: Jim Raykowski <rayk...@gmail.com> CommitDate: Wed Nov 9 08:23:42 2022 +0100 tdf#139633 SdNavigator: Enhancement to rename pages and objects This enhancement adds a context popup menu to the Navigator tree that has menu entry 'Rename...'. Renaming is done by direct editing in the tree. Change-Id: I392ea839110b942e1a336748c896d33ee5ece9b3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/141801 Tested-by: Jenkins Reviewed-by: Jim Raykowski <rayk...@gmail.com> diff --git a/sd/UIConfig_sdraw.mk b/sd/UIConfig_sdraw.mk index 136f84fcd315..abc068cff737 100644 --- a/sd/UIConfig_sdraw.mk +++ b/sd/UIConfig_sdraw.mk @@ -113,6 +113,7 @@ $(eval $(call gb_UIConfig_add_uifiles,modules/sdraw,\ sd/uiconfig/sdraw/ui/insertlayer \ sd/uiconfig/sdraw/ui/insertslidesdialog \ sd/uiconfig/sdraw/ui/namedesign \ + sd/uiconfig/sdraw/ui/navigatorcontextmenu \ sd/uiconfig/sdraw/ui/notebookbar \ sd/uiconfig/sdraw/ui/notebookbar_compact \ sd/uiconfig/sdraw/ui/notebookbar_single \ diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx index 162b3011a23d..98883e79999b 100644 --- a/sd/source/ui/dlg/navigatr.cxx +++ b/sd/source/ui/dlg/navigatr.cxx @@ -47,6 +47,8 @@ #include <DrawViewShell.hxx> #include <utility> +#include <vcl/commandevent.hxx> + /** * SdNavigatorWin - FloatingWindow */ @@ -68,6 +70,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)); + mxTlbObjects->connect_popup_menu(LINK(this, SdNavigatorWin, CommandHdl)); mxToolbox->connect_clicked(LINK(this, SdNavigatorWin, SelectToolboxHdl)); mxToolbox->connect_menu_toggled(LINK(this, SdNavigatorWin, DropdownClickToolBoxHdl)); @@ -238,6 +241,27 @@ IMPL_STATIC_LINK_NOARG(SdNavigatorWin, MouseReleaseHdl, const MouseEvent&, bool) return true; } +IMPL_LINK(SdNavigatorWin, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + weld::TreeView& rTreeView = GetObjects().get_treeview(); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&rTreeView, + "modules/sdraw/ui/navigatorcontextmenu.ui")); + std::unique_ptr<weld::Menu> xPop = xBuilder->weld_menu("navmenu"); + OString sCommand = xPop->popup_at_rect(&rTreeView, + tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + return true; +} + +void SdNavigatorWin::ExecuteContextMenuAction(std::string_view rSelectedPopupEntry) +{ + if (rSelectedPopupEntry == "rename") + GetObjects().start_editing(); +} + IMPL_LINK(SdNavigatorWin, SelectToolboxHdl, const OString&, rCommand, void) { PageJump ePage = PAGE_NONE; @@ -393,9 +417,13 @@ IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool) pWindow->GrabFocus(); if (!mxTlbObjects->IsNavigationGrabsFocus()) + { // This is the case when keyboard navigation inside the // navigator should continue to work. + if (mxNavigatorDlg) + mxNavigatorDlg->GrabFocus(); mxTlbObjects->grab_focus(); + } } } } @@ -687,7 +715,7 @@ IMPL_LINK(SdNavigatorWin, KeyInputHdl, const KeyEvent&, rKEvt, bool) if (KEY_ESCAPE == rKEvt.GetKeyCode().GetCode()) { // during drag'n'drop we just stop the drag but do not close the navigator - if (!SdPageObjsTLV::IsInDrag()) + if (!SdPageObjsTLV::IsInDrag() && !GetObjects().IsEditingActive()) { ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( mpBindings->GetDispatcher()->GetFrame()); if (pBase) diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx index 226f8fe11eac..0c0e5df03245 100644 --- a/sd/source/ui/dlg/sdtreelb.cxx +++ b/sd/source/ui/dlg/sdtreelb.cxx @@ -53,6 +53,8 @@ #include <comphelper/servicehelper.hxx> #include <comphelper/processfactory.hxx> +#include <svx/svditer.hxx> +#include <vcl/commandevent.hxx> using namespace com::sun::star; @@ -286,16 +288,44 @@ bool SdPageObjsTLV::IsEqualToDoc( const SdDrawDocument* pInDoc ) return !xEntry; } +IMPL_LINK(SdPageObjsTLV, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (IsEditingActive()) + return false; + + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + m_xTreeView->grab_focus(); + + // select clicked entry + if (std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + rCEvt.IsMouseEvent() && m_xTreeView->get_dest_row_at_pos( + rCEvt.GetMousePosPixel(), xEntry.get(), false)) + { + m_bSelectionHandlerNavigates = true; + m_bNavigationGrabsFocus = false; + m_xTreeView->set_cursor(*xEntry); + Select(); + } + + return m_aPopupMenuHdl.Call(rCEvt); + } + + return false; +} + IMPL_LINK(SdPageObjsTLV, KeyInputHdl, const KeyEvent&, rKEvt, bool) { const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); if (m_xAccel->execute(rKeyCode)) { + m_bEditing = false; // the accelerator consumed the event return true; } if (rKeyCode.GetCode() == KEY_RETURN) { + m_bEditing = false; std::unique_ptr<weld::TreeIter> xCursor(m_xTreeView->make_iterator()); if (m_xTreeView->get_cursor(xCursor.get()) && m_xTreeView->iter_has_child(*xCursor)) { @@ -309,11 +339,16 @@ IMPL_LINK(SdPageObjsTLV, KeyInputHdl, const KeyEvent&, rKEvt, bool) m_bNavigationGrabsFocus = false; return true; } - return m_aKeyPressHdl.Call(rKEvt); + bool bRet = m_aKeyPressHdl.Call(rKEvt); + // m_bEditing needs to be set after key press handler call back or x11 won't end editing on + // Esc key press. See SdNavigatorWin::KeyInputHdl. + m_bEditing = false; + return bRet; } IMPL_LINK(SdPageObjsTLV, MousePressHdl, const MouseEvent&, rMEvt, bool) { + m_bEditing = false; m_bSelectionHandlerNavigates = rMEvt.GetClicks() == 1; m_bNavigationGrabsFocus = rMEvt.GetClicks() != 1; return false; @@ -652,11 +687,85 @@ SdPageObjsTLV::SdPageObjsTLV(std::unique_ptr<weld::TreeView> xTreeView) m_xTreeView->connect_key_press(LINK(this, SdPageObjsTLV, KeyInputHdl)); m_xTreeView->connect_mouse_press(LINK(this, SdPageObjsTLV, MousePressHdl)); m_xTreeView->connect_mouse_release(LINK(this, SdPageObjsTLV, MouseReleaseHdl)); + m_xTreeView->connect_editing(LINK(this, SdPageObjsTLV, EditingEntryHdl), + LINK(this, SdPageObjsTLV, EditedEntryHdl)); + m_xTreeView->connect_popup_menu(LINK(this, SdPageObjsTLV, CommandHdl)); m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 28, m_xTreeView->get_text_height() * 8); } +IMPL_LINK(SdPageObjsTLV, EditEntryAgain, void*, p, void) +{ + m_xTreeView->grab_focus(); + std::unique_ptr<weld::TreeIter> xEntry(static_cast<weld::TreeIter*>(p)); + m_xTreeView->start_editing(*xEntry); + m_bEditing = true; +} + +IMPL_LINK_NOARG(SdPageObjsTLV, EditingEntryHdl, const weld::TreeIter&, bool) +{ + m_bEditing = true; + return true; +} + +IMPL_LINK(SdPageObjsTLV, EditedEntryHdl, const IterString&, rIterString, bool) +{ + m_bEditing = false; + + // Did the name change? + if (m_xTreeView->get_text(rIterString.first) == rIterString.second) + return true; + + // If the new name is empty or not unique, start editing again. + bool bUniqueName = true; + std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator()); + if (!rIterString.second.isEmpty()) + { + if (m_xTreeView->get_iter_first(*xEntry)) + { + do + { + // skip self! + if (m_xTreeView->iter_compare(*xEntry, rIterString.first) != 0 && + m_xTreeView->get_text(*xEntry) == rIterString.second) + { + bUniqueName = false; + break; + } + } while(m_xTreeView->iter_next(*xEntry)); + } + } + if (rIterString.second.isEmpty() || !bUniqueName) + { + m_xTreeView->copy_iterator(rIterString.first, *xEntry); + Application::PostUserEvent(LINK(this, SdPageObjsTLV, EditEntryAgain), xEntry.release()); + return false; + } + + // set the new name + const auto& rEntryId = m_xTreeView->get_id(rIterString.first); + if (rEntryId.toInt64() == 1) + { + // page name + if (::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh()) + { + if (::sd::ViewShell* pViewShell = GetViewShellForDocShell(*pDocShell)) + { + SdPage* pPage = pViewShell->GetActualPage(); + pPage->SetName(rIterString.second); + } + } + } + else if (SdrObject* pCursorEntryObject = weld::fromId<SdrObject*>(rEntryId)) + { + // object name + pCursorEntryObject->SetName(rIterString.second); + } + + return true; +} + IMPL_LINK_NOARG(SdPageObjsTLV, SelectHdl, weld::TreeView&, void) { if (m_nSelectEventId) @@ -683,6 +792,9 @@ void SdPageObjsTLV::Select() { m_nSelectEventId = nullptr; + if (IsEditingActive()) + return; + m_bLinkableSelected = true; m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ diff --git a/sd/source/ui/inc/navigatr.hxx b/sd/source/ui/inc/navigatr.hxx index f0b7d1fca5e8..fdcc1f2cf3ab 100644 --- a/sd/source/ui/inc/navigatr.hxx +++ b/sd/source/ui/inc/navigatr.hxx @@ -159,9 +159,12 @@ private: DECL_DLLPRIVATE_LINK( ShapeFilterCallback, const OString&, void ); DECL_DLLPRIVATE_LINK( KeyInputHdl, const KeyEvent&, bool ); DECL_DLLPRIVATE_STATIC_LINK(SdNavigatorWin, MouseReleaseHdl, const MouseEvent&, bool); + DECL_LINK(CommandHdl, const CommandEvent&, bool); void SetDragImage(); + void ExecuteContextMenuAction(std::string_view rSelectedPopupEntry); + public: //when object is marked , fresh the corresponding entry tree . void FreshTree ( const SdDrawDocument* pDoc ); diff --git a/sd/source/ui/inc/sdtreelb.hxx b/sd/source/ui/inc/sdtreelb.hxx index 38a797519654..d937f9e310c0 100644 --- a/sd/source/ui/inc/sdtreelb.hxx +++ b/sd/source/ui/inc/sdtreelb.hxx @@ -95,6 +95,8 @@ private: */ bool m_bNavigationGrabsFocus; + bool m_bEditing = false; + SelectionMode m_eSelectionMode; ImplSVEvent* m_nSelectEventId; @@ -106,6 +108,7 @@ private: Link<weld::TreeView&, bool> m_aRowActivatedHdl; Link<const KeyEvent&, bool> m_aKeyPressHdl; Link<const MouseEvent&, bool> m_aMouseReleaseHdl; + Link<const CommandEvent&, bool> m_aPopupMenuHdl; /** 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 @@ -134,6 +137,13 @@ private: DECL_DLLPRIVATE_LINK(DragBeginHdl, bool&, bool); DECL_DLLPRIVATE_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool); + typedef std::pair<const weld::TreeIter&, OUString> IterString; + DECL_LINK(EditedEntryHdl, const IterString&, bool); + DECL_LINK(EditEntryAgain, void*, void); + + DECL_LINK(CommandHdl, const CommandEvent&, bool); + /** Determine whether the specified page belongs to the current show which is either the standard show or a custom show. @param pPage @@ -156,6 +166,17 @@ public: SdPageObjsTLV(std::unique_ptr<weld::TreeView> xTreeview); ~SdPageObjsTLV(); + bool IsEditingActive() const {return m_bEditing;} + + void start_editing() + { + std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xIter.get())) + { + m_xTreeView->start_editing(*xIter); + } + } + void set_sensitive(bool bSensitive) { m_xTreeView->set_sensitive(bSensitive); @@ -222,6 +243,11 @@ public: m_aMouseReleaseHdl = rLink; } + void connect_popup_menu(const Link<const CommandEvent&, bool>& rLink) + { + m_aPopupMenuHdl = rLink; + } + bool HasSelectedChildren(std::u16string_view rName); bool SelectEntry(std::u16string_view rName); void SelectEntry(const SdrObject* pObj); @@ -256,6 +282,11 @@ public: return m_xTreeView->get_iter_first(rIter); } + weld::TreeView& get_treeview() + { + return *m_xTreeView; + } + std::unique_ptr<weld::TreeIter> make_iterator() { return m_xTreeView->make_iterator(); diff --git a/sd/uiconfig/sdraw/ui/navigatorcontextmenu.ui b/sd/uiconfig/sdraw/ui/navigatorcontextmenu.ui new file mode 100644 index 000000000000..746734e4e86f --- /dev/null +++ b/sd/uiconfig/sdraw/ui/navigatorcontextmenu.ui @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.38.2 --> +<interface domain="sd"> + <requires lib="gtk+" version="3.20"/> + <object class="GtkMenu" id="navmenu"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkMenuItem" id="rename"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes" context="navigatorcontextmenu|STR_RENAME">Rename...</property> + <property name="use-underline">True</property> + </object> + </child> + </object> +</interface>