tdf#30709 sw Style Inspector: show bookmark data

If the current character position is there in a bookmark
(or in multiple bookmarks), show bookmark names and RDF
metadata under the node "Bookmarks" in the Style Inspector.

Follow-up of commit dd45df62d217db59be3f515b6c7e1bfd17ec4350
"tdf#30709 sw: show RDF metadata in Style Inspector".

Change-Id: I943682bd13e7760a8f7a8d534f497e88f2769275
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114225
Tested-by: Jenkins
Reviewed-by: László Németh <nemeth@numbertext.org>
diff --git a/sw/qa/uitest/data/bookmark-metadata.odt b/sw/qa/uitest/data/bookmark-metadata.odt
new file mode 100644
index 0000000..3eec9ab
--- /dev/null
+++ b/sw/qa/uitest/data/bookmark-metadata.odt
Binary files differ
diff --git a/sw/qa/uitest/styleInspector/styleInspector.py b/sw/qa/uitest/styleInspector/styleInspector.py
index a869df19..6d531e9 100644
--- a/sw/qa/uitest/styleInspector/styleInspector.py
+++ b/sw/qa/uitest/styleInspector/styleInspector.py
@@ -189,4 +189,61 @@ class styleNavigator(UITestCase):
        self.xUITest.executeCommand(".uno:Sidebar")
        self.ui_test.close_doc()

    def test_bookmark_metadata(self):
        self.ui_test.load_file(get_url_for_data_file("bookmark-metadata.odt"))
        xWriterDoc = self.xUITest.getTopFocusWindow()
        xWriterEdit = xWriterDoc.getChild("writer_edit")

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

        xListBox = xWriterEdit.getChild('listbox_fonts')

        # The cursor is on text without metadata
        self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
        self.assertEqual("Default Paragraph Style", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
        self.assertEqual(136, len(xListBox.getChild('0').getChild('0').getChildren()))
        self.assertEqual(0, len(xListBox.getChild('1').getChildren()))
        self.assertEqual(0, len(xListBox.getChild('2').getChildren()))
        self.assertEqual(0, len(xListBox.getChild('3').getChildren()))

        self.xUITest.executeCommand(".uno:GoDown")

        # The cursor is on text with paragraph metadata showed under direct paragraph formatting
        self.assertEqual(1, len(xListBox.getChild('0').getChildren()))
        self.assertEqual("Default Paragraph Style", get_state_as_dict(xListBox.getChild('0').getChild('0'))['Text'])
        self.assertEqual(136, len(xListBox.getChild('0').getChild('0').getChildren()))

        # Outer bookmark
        xBookmarkFormatting = xListBox.getChild('4')
        self.assertEqual(1, len(xBookmarkFormatting.getChildren()))
        self.assertEqual("Bookmark 1", get_state_as_dict(xBookmarkFormatting.getChild('0'))['Text'])

        self.xUITest.executeCommand(".uno:GoDown")

        # Inner bookmark
        xBookmarkFormatting = xListBox.getChild('4')
        self.assertEqual(2, len(xBookmarkFormatting.getChildren()))

        self.assertEqual("Bookmark 1", get_state_as_dict(xBookmarkFormatting.getChild('0'))['Text'])
        xMetadata = xBookmarkFormatting.getChild('0').getChild('0')
        self.assertEqual(2, len(xMetadata.getChildren()))
        self.assertEqual("xml:id\tID-566430c5-9857-4ff2-be6d-57d127368d88", get_state_as_dict(xMetadata.getChild('0'))['Text'])
        self.assertEqual("http://www.w3.org/1999/02/22-rdf-syntax-ns#type\tBookmark", get_state_as_dict(xMetadata.getChild('1'))['Text'])

        self.assertEqual("Bookmark 2", get_state_as_dict(xBookmarkFormatting.getChild('1'))['Text'])
        xMetadata = xBookmarkFormatting.getChild('1').getChild('0')
        self.assertEqual(2, len(xMetadata.getChildren()))
        self.assertEqual("xml:id\tID-941142c3-924d-4884-a521-cb6a2dd26f04", get_state_as_dict(xMetadata.getChild('0'))['Text'])
        self.assertEqual("http://www.w3.org/1999/02/22-rdf-syntax-ns#type\tBookmark", get_state_as_dict(xMetadata.getChild('1'))['Text'])

        # Only in outer bookmark again
        self.xUITest.executeCommand(".uno:GoDown")
        xBookmarkFormatting = xListBox.getChild('4')
        self.assertEqual(1, len(xBookmarkFormatting.getChildren()))
        self.assertEqual("Bookmark 1", get_state_as_dict(xBookmarkFormatting.getChild('0'))['Text'])

        self.xUITest.executeCommand(".uno:Sidebar")
        self.ui_test.close_doc()

# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
index cac18da..a59143b 100644
--- a/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
+++ b/sw/source/uibase/sidebar/WriterInspectorTextPanel.cxx
@@ -25,7 +25,9 @@
#include <wrtsh.hxx>
#include <unoprnms.hxx>
#include <editeng/unoprnms.hxx>
#include <com/sun/star/text/XBookmarksSupplier.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/text/XTextRangeCompare.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/frame/XFrame.hpp>
@@ -502,15 +504,18 @@ static void UpdateTree(SwDocShell* pDocSh, std::vector<svx::sidebar::TreeNode>& 
    svx::sidebar::TreeNode aCharNode;
    svx::sidebar::TreeNode aParaNode;
    svx::sidebar::TreeNode aParaDFNode;
    svx::sidebar::TreeNode aBookmarksNode;

    aCharNode.sNodeName = SwResId(STR_CHARACTERSTYLEFAMILY);
    aParaNode.sNodeName = SwResId(STR_PARAGRAPHSTYLEFAMILY);
    aCharDFNode.sNodeName = SwResId(RID_CHAR_DIRECTFORMAT);
    aParaDFNode.sNodeName = SwResId(RID_PARA_DIRECTFORMAT);
    aBookmarksNode.sNodeName = SwResId(STR_CONTENT_TYPE_BOOKMARK);
    aCharDFNode.NodeType = svx::sidebar::TreeNode::Category;
    aCharNode.NodeType = svx::sidebar::TreeNode::Category;
    aParaNode.NodeType = svx::sidebar::TreeNode::Category;
    aParaDFNode.NodeType = svx::sidebar::TreeNode::Category;
    aBookmarksNode.NodeType = svx::sidebar::TreeNode::Category;

    uno::Reference<text::XTextRange> xRange(
        SwXTextRange::CreateXTextRange(*pDoc, *pCursor->GetPoint(), nullptr));
@@ -588,17 +593,54 @@ static void UpdateTree(SwDocShell* pDocSh, std::vector<svx::sidebar::TreeNode>& 
    std::reverse(aParaNode.children.begin(),
                 aParaNode.children.end()); // Parent style should be first then children

    // Collect bookmarks at character position
    uno::Reference<text::XBookmarksSupplier> xBookmarksSupplier(pDocSh->GetBaseModel(),
                                                                uno::UNO_QUERY);

    uno::Reference<container::XIndexAccess> xBookmarks(xBookmarksSupplier->getBookmarks(),
                                                       uno::UNO_QUERY);
    for (sal_Int32 i = 0; i < xBookmarks->getCount(); ++i)
    {
        svx::sidebar::TreeNode aCurNode;
        uno::Reference<text::XTextContent> bookmark;
        xBookmarks->getByIndex(i) >>= bookmark;
        uno::Reference<container::XNamed> xBookmark(bookmark, uno::UNO_QUERY);

        try
        {
            uno::Reference<text::XTextRange> bookmarkRange = bookmark->getAnchor();
            uno::Reference<text::XTextRangeCompare> xTextRangeCompare(xRange->getText(),
                                                                      uno::UNO_QUERY);
            if (xTextRangeCompare.is()
                && xTextRangeCompare->compareRegionStarts(bookmarkRange, xRange) != -1
                && xTextRangeCompare->compareRegionEnds(xRange, bookmarkRange) != -1)
            {
                aCurNode.sNodeName = xBookmark->getName();
                aCurNode.NodeType = svx::sidebar::TreeNode::ComplexProperty;

                MetadataToTreeNode(xBookmark, aCurNode);
                aBookmarksNode.children.push_back(aCurNode);
            }
        }
        catch (const lang::IllegalArgumentException&)
        {
        }
    }

    /*
    Display Order :-
    PARAGRAPH STYLE
    PARAGRAPH DIRECT FORMATTING
    CHARACTER STYLE
    DIRECT FORMATTING
    BOOKMARKS
    */
    aStore.push_back(aParaNode);
    aStore.push_back(aParaDFNode);
    aStore.push_back(aCharNode);
    aStore.push_back(aCharDFNode);
    if (aBookmarksNode.children.size() > 0)
        aStore.push_back(aBookmarksNode);
}

IMPL_LINK(WriterInspectorTextPanel, AttrChangedNotify, LinkParamNone*, pLink, void)