include/vcl/weld.hxx               |    4 
 sw/source/uibase/inc/conttree.hxx  |   10 ++
 sw/source/uibase/utlui/content.cxx |  170 +++++++++++++++++++++++++++++++++++++
 vcl/inc/salvtables.hxx             |    4 
 vcl/inc/treeglue.hxx               |   25 +++--
 vcl/source/app/salvtables.cxx      |    6 -
 vcl/unx/gtk3/gtkinst.cxx           |   41 ++++----
 7 files changed, 224 insertions(+), 36 deletions(-)

New commits:
commit 3cb654972ba204efb5dd35a44f95c7d509414400
Author:     Jim Raykowski <rayk...@gmail.com>
AuthorDate: Sat Dec 3 20:11:07 2022 -0900
Commit:     Jim Raykowski <rayk...@gmail.com>
CommitDate: Tue Dec 6 21:47:49 2022 +0000

    tdf#152029 Visually draw attention to in-view bookmark
    
    when mouse pointer is over bookmark entry in the Navigator content tree
    
    This patch brings attention to in-view bookmarks when the mouse pointer
    is positioned over bookmark content entries in the Navigator content
    tree. All in-view bookmarks are brought to attention by placing the
    mouse pointer over the bookmark content type (category) entry.
    
    The patch adds a parameter to the weld::get_dest_row_at_pos function to
    give the option not to auto scroll. It is needed to prevent auto
    scrolling when used in the the mouse move handler. Additional use can
    be made in the content tree CommandHdl to make the tree not jump when
    the context popup menu is activated for entries near the top and bottom
    of the the visible tree.
    
    Change-Id: I04e306286ca58ab6c8ae7e5c02b25a0592c4a9d3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143628
    Tested-by: Jenkins
    Reviewed-by: Jim Raykowski <rayk...@gmail.com>

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 <o3tl/enumarray.hxx>
 #include <o3tl/typed_flags_set.hxx>
 
+#include <svx/sdr/overlay/overlayobject.hxx>
+
 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<ContentTypeId,std::unique_ptr<SwContentType>>  
m_aActiveContentArr;
     o3tl::enumarray<ContentTypeId,std::unique_ptr<SwContentType>>  
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<weld::TreeIter> m_xOverlayCompareEntry;
+    std::unique_ptr<sdr::overlay::OverlayObject> m_xOverlayObject;
+
+    void BringBookmarksToAttention(const std::vector<OUString>& 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<weld::TreeView> 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 <txtftn.hxx>
 #include <fmtftn.hxx>
 
+#include <txtfrm.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+
 #define CTYPE_CNT   0
 #define CTYPE_CTT   1
 
@@ -1066,6 +1072,7 @@ 
SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> 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<weld::TreeView> 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<weld::TreeView> 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<ContentTypeId>())
     {
@@ -1121,6 +1130,8 @@ 
SwContentTree::SwContentTree(std::unique_ptr<weld::TreeView> 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<weld::TreeIter> 
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<SwContent*>(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<OUString> 
{pCnt->GetName()});
+                }
+            }
+        }
+        else // content type entry
+        {
+            const ContentTypeId nType =
+                    
weld::fromId<SwContentType*>(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<frame::XModel> xModel =
+                            
m_pActiveShell->GetView().GetDocShell()->GetBaseModel();
+                    Reference<text::XBookmarksSupplier> xBkms(xModel, 
uno::UNO_QUERY);
+                    Reference<container::XNameAccess> xNames = 
xBkms->getBookmarks();
+                    if (xNames.is())
+                    {
+                        auto 
aNames(comphelper::sequenceToContainer<std::vector<OUString>>(
+                                        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<sdr::overlay::OverlayManager>& 
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<OUString>& 
rNames)
+{
+    std::vector<basegfx::B2DRange> 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<const 
SwTextFrame*>(
+                            
pMarkStartTextNode->getLayoutFrame(m_pActiveShell->GetLayout())))
+                {
+                    SwPosition aMarkEnd = (*ppBkmk)->GetMarkEnd();
+                    if (const SwTextNode* pMarkEndTextNode = 
aMarkEnd.GetNode().GetTextNode())
+                    {
+                        if (const SwTextFrame* pMarkEndFrame = 
static_cast<const SwTextFrame*>(
+                                    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<LclTabListBox*>(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;

Reply via email to