tdf#159428 Prefer heading over bookmarks in Navigator

When a bookmark is in a heading, highlighting the heading
is more important than highlighting the bookmark.

Change-Id: I6348e42b14ece226a25961bd3145193645972e79
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162694
Tested-by: Jenkins
Reviewed-by: Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>
diff --git a/sw/qa/uitest/data/tdf159428.odt b/sw/qa/uitest/data/tdf159428.odt
new file mode 100644
index 0000000..6b30d6bf
--- /dev/null
+++ b/sw/qa/uitest/data/tdf159428.odt
Binary files differ
diff --git a/sw/qa/uitest/navigator/tdf159428.py b/sw/qa/uitest/navigator/tdf159428.py
new file mode 100644
index 0000000..3a8634a
--- /dev/null
+++ b/sw/qa/uitest/navigator/tdf159428.py
@@ -0,0 +1,49 @@
# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
#
# This file is part of the LibreOffice project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#

from uitest.framework import UITestCase
from libreoffice.uno.propertyvalue import mkPropertyValues
from uitest.uihelper.common import get_state_as_dict, get_url_for_data_file

class tdf159428(UITestCase):

    def test_tdf159428(self):
        global selectionChangedResult
        with self.ui_test.load_file(get_url_for_data_file('tdf159428.odt')):
            xWriterDoc = self.xUITest.getTopFocusWindow()
            xWriterEdit = xWriterDoc.getChild("writer_edit")

            self.xUITest.executeCommand(".uno:Sidebar")
            xWriterEdit.executeAction("SIDEBAR", mkPropertyValues({"PANEL": "SwNavigatorPanel"}))

            xNavigatorPanel = self.ui_test.wait_until_child_is_available('NavigatorPanel')
            xContentTree = xNavigatorPanel.getChild("contenttree")

            # select fist bookmark - Heading "H1" should be tracked
            self.xUITest.executeCommand(".uno:GoToNextPara")
            self.xUITest.executeCommand(".uno:GoToNextPara")
            xWriterEdit.executeAction("SELECT", mkPropertyValues({"START_POS": "111", "END_POS": "119"}))
            self.ui_test.wait_until_property_is_updated(xContentTree, "SelectEntryText", "H1")
            self.assertEqual(get_state_as_dict(xContentTree)["SelectEntryText"], "H1")

            # select second bookmark - Heading "H1 mit Lesezeichen" should be tracked
            self.xUITest.executeCommand(".uno:GoToNextPara")
            xWriterEdit.executeAction("SELECT", mkPropertyValues({"START_POS": "0", "END_POS": "18"}))
            self.ui_test.wait_until_property_is_updated(xContentTree, "SelectEntryText", "H1 mit Lesezeichen")
            self.assertEqual(get_state_as_dict(xContentTree)["SelectEntryText"], "H1 mit Lesezeichen")

            # select third bookmark - no heading tracked
            self.xUITest.executeCommand(".uno:GoToPrevPara")
            self.xUITest.executeCommand(".uno:GoToPrevPara")
            self.xUITest.executeCommand(".uno:GoToPrevPara")
            xWriterEdit.executeAction("SELECT", mkPropertyValues({"START_POS": "141", "END_POS": "146"}))
            self.ui_test.wait_until_property_is_updated(xContentTree, "SelectEntryText", "Lesezeichen 3")
            self.assertEqual(get_state_as_dict(xContentTree)["SelectEntryText"], "Lesezeichen 3")

# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/uibase/utlui/content.cxx b/sw/source/uibase/utlui/content.cxx
index 5655b67..b058cf4 100644
--- a/sw/source/uibase/utlui/content.cxx
+++ b/sw/source/uibase/utlui/content.cxx
@@ -4305,52 +4305,6 @@ void SwContentTree::UpdateTracking()
                                                  aContentAtPos.pFndTextAttr);
            return;
        }
        // bookmarks - track first bookmark at cursor
        if (mTrackContentType[ContentTypeId::BOOKMARK] &&
                (m_pActiveShell->GetSelectionType() & SelectionType::Text))
        {
            SwPaM* pCursor = m_pActiveShell->GetCursor();
            IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
            IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
            if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd() &&
                    !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK))
            {
                OUString sBookmarkName;
                SwPosition* pCursorPoint = pCursor->GetPoint();
                while (ppBookmark != pMarkAccess->getBookmarksEnd())
                {
                    if (lcl_IsUiVisibleBookmark(*ppBookmark) &&
                            *pCursorPoint >= (*ppBookmark)->GetMarkStart() &&
                            *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
                    {
                        sBookmarkName = (*ppBookmark)->GetName();
                        // keep previously selected bookmark instead
                        // of selecting a different bookmark inside of it
                        if (sBookmarkName == m_sSelectedItem)
                            break;
                    }
                    else if (!sBookmarkName.isEmpty() &&
                        *pCursorPoint < (*ppBookmark)->GetMarkStart())
                    {
                        // don't search a different bookmark inside the
                        // previous one, if the starting position of the next bookmarks
                        // is after the cursor position (assuming that the
                        // bookmark iterator jumps inside the same text by positions)
                        break;
                    }
                    ++ppBookmark;
                }

                if (!sBookmarkName.isEmpty())
                {
                    // select the bookmark
                    lcl_SelectByContentTypeAndName(this, *m_xTreeView,
                                                       SwResId(STR_CONTENT_TYPE_BOOKMARK),
                                                       sBookmarkName);
                    return;
                }
            }
        }
        // references
        if (SwContentAtPos aContentAtPos(IsAttrAtPos::RefMark);
                m_pActiveShell->GetContentAtPos(m_pActiveShell->GetCursorDocPos(), aContentAtPos) &&
@@ -4440,12 +4394,11 @@ void SwContentTree::UpdateTracking()
            // is not the current section
        }
    }
    // outline
    if (m_nOutlineTracking == 3)
        return;
    // find out where the cursor is
    const SwOutlineNodes::size_type nActPos = GetWrtShell()->GetOutlinePos(MAXLEVEL);
    if (!((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE) || nActPos == SwOutlineNodes::npos))
    if (m_nOutlineTracking != 3
        && !((m_bIsRoot && m_nRootType != ContentTypeId::OUTLINE)
             || nActPos == SwOutlineNodes::npos))
    {
        // assure outline content type is expanded
        // this assumes outline content type is first in treeview
@@ -4527,17 +4480,63 @@ void SwContentTree::UpdateTracking()
            }
            return bRet;
        });
        return;
    }
    else

    // bookmarks - track first bookmark at cursor
    // tdf#159428 Only when no outline found. Showing the outline is more important than
    // showing a bookmark at the cursor position.
    if (mTrackContentType[ContentTypeId::BOOKMARK] &&
            (m_pActiveShell->GetSelectionType() & SelectionType::Text))
    {
        // clear treeview selections
        if (m_xTreeView->count_selected_rows() > 0)
        SwPaM* pCursor = m_pActiveShell->GetCursor();
        IDocumentMarkAccess* const pMarkAccess = m_pActiveShell->getIDocumentMarkAccess();
        IDocumentMarkAccess::const_iterator_t ppBookmark = pMarkAccess->getBookmarksBegin();
        if (pCursor && ppBookmark != pMarkAccess->getBookmarksEnd()
            && !(m_bIsRoot && m_nRootType != ContentTypeId::BOOKMARK))
        {
            m_xTreeView->unselect_all();
            m_xTreeView->set_cursor(-1);
            Select();
            OUString sBookmarkName;
            SwPosition* pCursorPoint = pCursor->GetPoint();
            while (ppBookmark != pMarkAccess->getBookmarksEnd())
            {
                if (lcl_IsUiVisibleBookmark(*ppBookmark)
                    && *pCursorPoint >= (*ppBookmark)->GetMarkStart()
                    && *pCursorPoint <= (*ppBookmark)->GetMarkEnd())
                {
                    sBookmarkName = (*ppBookmark)->GetName();
                    // keep previously selected bookmark instead
                    // of selecting a different bookmark inside of it
                    if (sBookmarkName == m_sSelectedItem)
                        break;
                }
                else if (!sBookmarkName.isEmpty() && *pCursorPoint < (*ppBookmark)->GetMarkStart())
                {
                    // don't search a different bookmark inside the
                    // previous one, if the starting position of the next bookmarks
                    // is after the cursor position (assuming that the
                    // bookmark iterator jumps inside the same text by positions)
                    break;
                }
                ++ppBookmark;
            }

            if (!sBookmarkName.isEmpty())
            {
                // select the bookmark
                lcl_SelectByContentTypeAndName(this, *m_xTreeView,
                                               SwResId(STR_CONTENT_TYPE_BOOKMARK), sBookmarkName);
                return;
            }
        }
    }

    // clear treeview selections
    if (m_xTreeView->count_selected_rows() > 0)
    {
        m_xTreeView->unselect_all();
        m_xTreeView->set_cursor(-1);
        Select();
    }
}

void SwContentTree::SelectOutlinesWithSelection()