diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 4b4ea74f13b3..3bcbc0a7a08d 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -1352,7 +1352,9 @@ public: * after the row * b) dnd highlight the dest row */ - virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, bool bDnDMode) = 0; + virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, bool bDnDMode, + bool bAutoScroll = true) + = 0; virtual void unset_drag_dest_row() = 0; virtual tools::Rectangle get_row_area(const weld::TreeIter& rIter) const = 0; // for dragging and dropping between TreeViews, return the active source diff --git a/sw/source/uibase/inc/conttree.hxx b/sw/source/uibase/inc/conttree.hxx index cf7c383f81e4..77358b9edb30 100644 --- a/sw/source/uibase/inc/conttree.hxx +++ b/sw/source/uibase/inc/conttree.hxx @@ -32,6 +32,8 @@ #include #include +#include + class SwWrtShell; class SwContentType; class SwNavigationPI; @@ -91,6 +93,7 @@ class SwContentTree final : public SfxListener SwNavigationPI* m_pDialog; OUString m_sSpace; AutoTimer m_aUpdTimer; + AutoTimer m_aOverlayObjectDelayTimer; o3tl::enumarray> m_aActiveContentArr; o3tl::enumarray> m_aHiddenContentArr; @@ -129,6 +132,11 @@ class SwContentTree final : public SfxListener bool m_bDocHasChanged = true; bool m_bIgnoreDocChange = false; // used to prevent tracking update + std::unique_ptr m_xOverlayCompareEntry; + std::unique_ptr m_xOverlayObject; + + void BringBookmarksToAttention(const std::vector& rNames); + /** * Before any data will be deleted, the last active entry has to be found. * After this the UserData will be deleted @@ -183,6 +191,8 @@ class SwContentTree final : public SfxListener DECL_LINK(QueryTooltipHdl, const weld::TreeIter&, OUString); DECL_LINK(DragBeginHdl, bool&, bool); DECL_LINK(TimerUpdate, Timer *, void); + DECL_LINK(m_aOverlayObjectDelayTimerHdl, Timer *, void); + DECL_LINK(MouseMoveHdl, const MouseEvent&, bool); public: SwContentTree(std::unique_ptr xTreeView, SwNavigationPI* pDialog); diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx index 32a9e8ab63ce..cb7f96ec3b23 100644 --- a/sw/source/uibase/utlui/content.cxx +++ b/sw/source/uibase/utlui/content.cxx @@ -102,6 +102,12 @@ #include #include +#include +#include +#include +#include +#include + #define CTYPE_CNT 0 #define CTYPE_CTT 1 @@ -1066,6 +1072,7 @@ SwContentTree::SwContentTree(std::unique_ptr xTreeView, SwNaviga , m_pDialog(pDialog) , m_sSpace(OUString(" ")) , m_aUpdTimer("SwContentTree m_aUpdTimer") + , m_aOverlayObjectDelayTimer("SwContentTree m_aOverlayObjectDelayTimer") , m_sInvisible(SwResId(STR_INVISIBLE)) , m_pHiddenShell(nullptr) , m_pActiveShell(nullptr) @@ -1082,6 +1089,7 @@ SwContentTree::SwContentTree(std::unique_ptr xTreeView, SwNaviga , m_bIsLastReadOnly(false) , m_bIsOutlineMoveable(true) , m_bViewHasChanged(false) + , m_xOverlayCompareEntry(m_xTreeView->make_iterator()) { m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 30, m_xTreeView->get_text_height() * 14); @@ -1097,6 +1105,7 @@ SwContentTree::SwContentTree(std::unique_ptr xTreeView, SwNaviga m_xTreeView->connect_popup_menu(LINK(this, SwContentTree, CommandHdl)); m_xTreeView->connect_query_tooltip(LINK(this, SwContentTree, QueryTooltipHdl)); m_xTreeView->connect_drag_begin(LINK(this, SwContentTree, DragBeginHdl)); + m_xTreeView->connect_mouse_move(LINK(this, SwContentTree, MouseMoveHdl)); for (ContentTypeId i : o3tl::enumrange()) { @@ -1121,6 +1130,8 @@ SwContentTree::SwContentTree(std::unique_ptr xTreeView, SwNaviga m_aUpdTimer.SetInvokeHandler(LINK(this, SwContentTree, TimerUpdate)); m_aUpdTimer.SetTimeout(1000); + m_aOverlayObjectDelayTimer.SetInvokeHandler(LINK(this, SwContentTree, m_aOverlayObjectDelayTimerHdl)); + m_aOverlayObjectDelayTimer.SetTimeout(500); } SwContentTree::~SwContentTree() @@ -1135,6 +1146,74 @@ SwContentTree::~SwContentTree() SetActiveShell(nullptr); } +IMPL_LINK(SwContentTree, MouseMoveHdl, const MouseEvent&, rMEvt, bool) +{ + if (rMEvt.IsEnterWindow()) + { + m_xTreeView->get_iter_first(*m_xOverlayCompareEntry); + return false; + } + bool bRemoveOverlayObject = false; + if (rMEvt.IsLeaveWindow()) + { + bRemoveOverlayObject = true; + } + else if (std::unique_ptr xEntry(m_xTreeView->make_iterator()); + m_xTreeView->get_dest_row_at_pos(rMEvt.GetPosPixel(), xEntry.get(), false, false)) + { + if (lcl_IsContent(*xEntry, *m_xTreeView)) // content entry + { + SwContent* pCnt = weld::fromId(m_xTreeView->get_id(*xEntry)); + const ContentTypeId nType = pCnt->GetParent()->GetType(); + bRemoveOverlayObject = nType != ContentTypeId::BOOKMARK; + if (!bRemoveOverlayObject && + m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) != 0) + { + m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry); + if (nType == ContentTypeId::BOOKMARK) + { + BringBookmarksToAttention(std::vector {pCnt->GetName()}); + } + } + } + else // content type entry + { + const ContentTypeId nType = + weld::fromId(m_xTreeView->get_id(*xEntry))->GetType(); + bRemoveOverlayObject = nType != ContentTypeId::BOOKMARK; + if (!bRemoveOverlayObject && + m_xTreeView->iter_compare(*xEntry, *m_xOverlayCompareEntry) != 0) + { + m_xTreeView->copy_iterator(*xEntry, *m_xOverlayCompareEntry); + if (nType == ContentTypeId::BOOKMARK) + { + Reference xModel = + m_pActiveShell->GetView().GetDocShell()->GetBaseModel(); + Reference xBkms(xModel, uno::UNO_QUERY); + Reference xNames = xBkms->getBookmarks(); + if (xNames.is()) + { + auto aNames(comphelper::sequenceToContainer>( + xNames->getElementNames())); + BringBookmarksToAttention(aNames); + } + } + } + } + } + if (bRemoveOverlayObject) + { + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject && m_xOverlayObject->getOverlayManager()) + { + m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject); + m_xOverlayObject.reset(); + } + m_xTreeView->get_iter_first(*m_xOverlayCompareEntry); + } + return false; +} + // Drag&Drop methods IMPL_LINK(SwContentTree, DragBeginHdl, bool&, rUnsetDragIcon, bool) { @@ -4836,6 +4915,23 @@ void SwContentTree::ShowActualView() GetParentWindow()->UpdateListBox(); } +IMPL_LINK_NOARG(SwContentTree, m_aOverlayObjectDelayTimerHdl, Timer *, void) +{ + m_aOverlayObjectDelayTimer.Stop(); + if (m_xOverlayObject) + { + if (SdrView* pView = m_pActiveShell->GetDrawView()) + { + if (SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(0)) + { + const rtl::Reference& xOverlayManager = + pPaintWindow->GetOverlayManager(); + xOverlayManager->add(*m_xOverlayObject); + } + } + } +} + IMPL_LINK_NOARG(SwContentTree, SelectHdl, weld::TreeView&, void) { if (m_pConfig->IsNavigateOnSelect()) @@ -5407,4 +5503,78 @@ void SwContentTree::SelectContentType(std::u16string_view rContentTypeName) } while (m_xTreeView->iter_next_sibling(*xIter)); } +void SwContentTree::BringBookmarksToAttention(const std::vector& rNames) +{ + std::vector aRanges; + IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess(); + for (const auto& rName : rNames) + { + IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark(rName); + if (ppBkmk != pMarkAccess->getBookmarksEnd()) + { + SwPosition aMarkStart = (*ppBkmk)->GetMarkStart(); + if (const SwTextNode* pMarkStartTextNode = aMarkStart.GetNode().GetTextNode()) + { + if (const SwTextFrame* pMarkStartFrame = static_cast( + pMarkStartTextNode->getLayoutFrame(m_pActiveShell->GetLayout()))) + { + SwPosition aMarkEnd = (*ppBkmk)->GetMarkEnd(); + if (const SwTextNode* pMarkEndTextNode = aMarkEnd.GetNode().GetTextNode()) + { + if (const SwTextFrame* pMarkEndFrame = static_cast( + pMarkEndTextNode->getLayoutFrame( + m_pActiveShell->GetLayout()))) + { + // adjust span when mark start equals mark end + if (aMarkStart == aMarkEnd) + { + if (aMarkEnd.GetContentIndex() < pMarkEndTextNode->Len() - 1) + aMarkEnd.AdjustContent(+1); + else if (aMarkStart.GetContentIndex() > 0) + aMarkStart.AdjustContent(-1); + } + SwRect aStartCharRect; + pMarkStartFrame->GetCharRect(aStartCharRect, aMarkStart); + SwRect aEndCharRect; + pMarkEndFrame->GetCharRect(aEndCharRect, aMarkEnd); + if (aStartCharRect.Top() == aEndCharRect.Top()) + { + // single line range + aRanges.emplace_back(aStartCharRect.Left(), + aStartCharRect.Top(), + aEndCharRect.Right() + 1, + aEndCharRect.Bottom() + 1); + } + else + { + // multi line range + SwRect aMarkStartFrameRect = pMarkStartFrame->getFrameArea(); + aRanges.emplace_back(aStartCharRect.Left(), + aStartCharRect.Top(), + aMarkStartFrameRect.Right(), + aStartCharRect.Bottom() + 1); + if (aStartCharRect.Bottom() + 1 != aEndCharRect.Top()) + aRanges.emplace_back(aMarkStartFrameRect.Left(), + aStartCharRect.Bottom() + 1, + aMarkStartFrameRect.Right(), + aEndCharRect.Top() + 1); + aRanges.emplace_back(aMarkStartFrameRect.Left(), + aEndCharRect.Top() + 1, + aEndCharRect.Right() + 1, + aEndCharRect.Bottom() + 1); + } + } + } + } + } + } + } + if (m_xOverlayObject && m_xOverlayObject->getOverlayManager()) + m_xOverlayObject->getOverlayManager()->remove(*m_xOverlayObject); + m_xOverlayObject.reset(new sdr::overlay::OverlaySelection(sdr::overlay::OverlayType::Invert, + Color(), std::move(aRanges), + true /*unused for Invert type*/)); + m_aOverlayObjectDelayTimer.Start(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index 7ad7a145e8af..d6968e36d00c 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -1716,8 +1716,8 @@ public: SvTabListBox& getTreeView(); - virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, - bool bDnDMode) override; + virtual bool get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, bool bDnDMode, + bool bAutoScroll = true) override; virtual void unset_drag_dest_row() override; diff --git a/vcl/inc/treeglue.hxx b/vcl/inc/treeglue.hxx index 4cb120d81f7f..d445a533b43f 100644 --- a/vcl/inc/treeglue.hxx +++ b/vcl/inc/treeglue.hxx @@ -121,26 +121,29 @@ public: m_aModelChangedHdl.Call(this); } - SvTreeListEntry* GetTargetAtPoint(const Point& rPos, bool bHighLightTarget) + SvTreeListEntry* GetTargetAtPoint(const Point& rPos, bool bHighLightTarget, bool bScroll = true) { SvTreeListEntry* pOldTargetEntry = pTargetEntry; pTargetEntry = PosOverBody(rPos) ? pImpl->GetEntry(rPos) : nullptr; if (pOldTargetEntry != pTargetEntry) ImplShowTargetEmphasis(pOldTargetEntry, false); - // scroll - if (rPos.Y() < 12) + if (bScroll) { - ImplShowTargetEmphasis(pTargetEntry, false); - ScrollOutputArea(+1); - } - else - { - Size aSize(pImpl->GetOutputSize()); - if (rPos.Y() > aSize.Height() - 12) + // scroll + if (rPos.Y() < 12) { ImplShowTargetEmphasis(pTargetEntry, false); - ScrollOutputArea(-1); + ScrollOutputArea(+1); + } + else + { + Size aSize(pImpl->GetOutputSize()); + if (rPos.Y() > aSize.Height() - 12) + { + ImplShowTargetEmphasis(pTargetEntry, false); + ScrollOutputArea(-1); + } } } diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index 39ee33de947f..733d78179500 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -5154,12 +5154,12 @@ void SalInstanceTreeView::set_sort_column(int nColumn) SvTabListBox& SalInstanceTreeView::getTreeView() { return *m_xTreeView; } bool SalInstanceTreeView::get_dest_row_at_pos(const Point& rPos, weld::TreeIter* pResult, - bool bDnDMode) + bool bDnDMode, bool bAutoScroll) { LclTabListBox* pTreeView = !bDnDMode ? dynamic_cast(m_xTreeView.get()) : nullptr; - SvTreeListEntry* pTarget - = pTreeView ? pTreeView->GetTargetAtPoint(rPos, false) : m_xTreeView->GetDropTarget(rPos); + SvTreeListEntry* pTarget = pTreeView ? pTreeView->GetTargetAtPoint(rPos, false, bAutoScroll) + : m_xTreeView->GetDropTarget(rPos); if (pTarget && pResult) { diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index e523e871290d..61d3f001c6d5 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -16240,7 +16240,7 @@ public: weld::TreeView::connect_popup_menu(rLink); } - virtual bool get_dest_row_at_pos(const Point &rPos, weld::TreeIter* pResult, bool bDnDMode) override + virtual bool get_dest_row_at_pos(const Point &rPos, weld::TreeIter* pResult, bool bDnDMode, bool bAutoScroll) override { if (rPos.X() < 0 || rPos.Y() < 0) { @@ -16311,28 +16311,31 @@ public: gtk_tree_path_free(path); gtk_tree_path_free(lastpath); - // auto scroll if we're close to the edges - GtkAdjustment* pVAdjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(m_pTreeView)); - double fStep = gtk_adjustment_get_step_increment(pVAdjustment); - if (rPos.Y() < fStep) + if (bAutoScroll) { - double fValue = gtk_adjustment_get_value(pVAdjustment) - fStep; - if (fValue < 0) - fValue = 0.0; - gtk_adjustment_set_value(pVAdjustment, fValue); - } - else - { - GdkRectangle aRect; - gtk_tree_view_get_visible_rect(m_pTreeView, &aRect); - if (rPos.Y() > aRect.height - fStep) + // auto scroll if we're close to the edges + GtkAdjustment* pVAdjustment = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(m_pTreeView)); + double fStep = gtk_adjustment_get_step_increment(pVAdjustment); + if (rPos.Y() < fStep) { - double fValue = gtk_adjustment_get_value(pVAdjustment) + fStep; - double fMax = gtk_adjustment_get_upper(pVAdjustment); - if (fValue > fMax) - fValue = fMax; + double fValue = gtk_adjustment_get_value(pVAdjustment) - fStep; + if (fValue < 0) + fValue = 0.0; gtk_adjustment_set_value(pVAdjustment, fValue); } + else + { + GdkRectangle aRect; + gtk_tree_view_get_visible_rect(m_pTreeView, &aRect); + if (rPos.Y() > aRect.height - fStep) + { + double fValue = gtk_adjustment_get_value(pVAdjustment) + fStep; + double fMax = gtk_adjustment_get_upper(pVAdjustment); + if (fValue > fMax) + fValue = fMax; + gtk_adjustment_set_value(pVAdjustment, fValue); + } + } } return ret;