sw/inc/IDocumentMarkAccess.hxx     |    2 +
 sw/qa/uibase/uno/uno.cxx           |   32 ++++++++++++++++++++++++++
 sw/source/core/doc/docbm.cxx       |   23 +++++++++++++++++++
 sw/source/core/inc/MarkManager.hxx |    1 
 sw/source/uibase/uno/loktxdoc.cxx  |   44 +++++++++++++++++++++++++++++++++++--
 5 files changed, 100 insertions(+), 2 deletions(-)

New commits:
commit d7c10661a7b4f6d9d28d2a43829c3c28f590fbaa
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Jan 11 10:38:05 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Jan 12 16:03:32 2023 +0000

    sw lok: expose name of bookmark under cursor
    
    It was possible to get the names of all bookmarks, but you could not get
    the name of the (innermost) bookmark under the current cursor.
    
    Getting the name of the current bookmark is useful for Zotero: if we
    already have a citation and want to insert one more, then we should turn
    the current citation into a citation cluster.
    
    Fix the problem by adding an API similar to what commit
    bb20dee2ef1b0804065e1cda2c834d257fdd90ed (sw lok: expose field type &
    command of fieldmark under cursor, 2023-01-05) did, but here we deal
    with bookmarks, not fieldmarks.
    
    Handle the actual bookmark lookup in MarkManager, such functionality
    looks useful outside LOK as well.
    
    (cherry picked from commit 4bcb66ec7b417fbe113267f2615e78fe47eb55ca)
    
    Change-Id: Ic5b9b36fda243c5d7d360fa03745b3e121b67b06
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145348
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Justin Luth <jl...@mail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/inc/IDocumentMarkAccess.hxx b/sw/inc/IDocumentMarkAccess.hxx
index d7f3bdd6674b..9ff1a311768a 100644
--- a/sw/inc/IDocumentMarkAccess.hxx
+++ b/sw/inc/IDocumentMarkAccess.hxx
@@ -314,6 +314,8 @@ class IDocumentMarkAccess
         */
         virtual const_iterator_t findFirstBookmarkStartsAfter(const 
SwPosition& rPos) const =0;
 
+        /// Get the innermost bookmark that contains rPos.
+        virtual sw::mark::IMark* getBookmarkFor(const SwPosition& rPos) const 
= 0;
 
         // Fieldmarks
         /** returns a STL-like random access iterator to the begin of the 
sequence of fieldmarks.
diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx
index 300278717664..595987eea423 100644
--- a/sw/qa/uibase/uno/uno.cxx
+++ b/sw/qa/uibase/uno/uno.cxx
@@ -336,6 +336,38 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetSections)
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), 
aTree.get_child("sections").count(""));
 }
 
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetBookmark)
+{
+    // Given a document with a bookmark:
+    SwDoc* pDoc = createSwDoc();
+    uno::Sequence<css::beans::PropertyValue> aArgs = {
+        comphelper::makePropertyValue("Bookmark", 
uno::Any(OUString("ZOTERO_BREF_1"))),
+        comphelper::makePropertyValue("BookmarkText", 
uno::Any(OUString("<p>aaa</p><p>bbb</p>"))),
+    };
+    dispatchCommand(mxComponent, ".uno:InsertBookmark", aArgs);
+
+    // When stepping into the bookmark with the cursor and getting the command 
value for
+    // .uno:Bookmark:
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    pWrtShell->Left(CRSR_SKIP_CHARS, /*bSelect=*/false, 1, 
/*bBasicCall=*/false);
+    tools::JsonWriter aJsonWriter;
+    OString aCommand(".uno:Bookmark?namePrefix=ZOTERO_BREF_");
+    auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    pXTextDocument->getCommandValues(aJsonWriter, aCommand);
+
+    // Then make sure we find the inserted bookmark:
+    std::unique_ptr<char[], o3tl::free_delete> 
pJSON(aJsonWriter.extractData());
+    std::stringstream aStream(pJSON.get());
+    boost::property_tree::ptree aTree;
+    boost::property_tree::read_json(aStream, aTree);
+    boost::property_tree::ptree aBookmark = aTree.get_child("bookmark");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - No such node (bookmark)
+    // i.e. the returned JSON was an empty object.
+    CPPUNIT_ASSERT_EQUAL(std::string("ZOTERO_BREF_1"), 
aBookmark.get<std::string>("name"));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx
index bc9884c2829e..fcce59889b48 100644
--- a/sw/source/core/doc/docbm.cxx
+++ b/sw/source/core/doc/docbm.cxx
@@ -1421,6 +1421,29 @@ namespace sw::mark
         return dynamic_cast<IFieldmark*>(pFieldmark);
     }
 
+    IMark* MarkManager::getBookmarkFor(const SwPosition& rPos) const
+    {
+        auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(),
+                               [&rPos](const sw::mark::MarkBase* pMark)
+                               { return pMark->IsCoveringPosition(rPos); });
+        if (it == m_vBookmarks.end())
+        {
+            return nullptr;
+        }
+        sw::mark::IMark* pBookmark = *it;
+        for (; it != m_vBookmarks.end() && (*it)->GetMarkStart() <= rPos; ++it)
+        {
+            // Find the innermost bookmark.
+            if (rPos < (*it)->GetMarkEnd()
+                && (pBookmark->GetMarkStart() < (*it)->GetMarkStart()
+                    || (*it)->GetMarkEnd() < pBookmark->GetMarkEnd()))
+            {
+                pBookmark = *it;
+            }
+        }
+        return pBookmark;
+    }
+
     void MarkManager::deleteFieldmarkAt(const SwPosition& rPos)
     {
         auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos));
diff --git a/sw/source/core/inc/MarkManager.hxx 
b/sw/source/core/inc/MarkManager.hxx
index 0cf40191e3d1..6c84d1a4c4b3 100644
--- a/sw/source/core/inc/MarkManager.hxx
+++ b/sw/source/core/inc/MarkManager.hxx
@@ -84,6 +84,7 @@ namespace sw::mark {
             virtual sal_Int32 getBookmarksCount() const override;
             virtual const_iterator_t findBookmark(const OUString& rName) const 
override;
             virtual const_iterator_t findFirstBookmarkStartsAfter(const 
SwPosition& rPos) const override;
+            virtual ::sw::mark::IMark* getBookmarkFor(const SwPosition& rPos) 
const override;
 
             // Fieldmarks
             virtual const_iterator_t getFieldmarksBegin() const override;
diff --git a/sw/source/uibase/uno/loktxdoc.cxx 
b/sw/source/uibase/uno/loktxdoc.cxx
index 6d5ddd675c23..101f0ff082c8 100644
--- a/sw/source/uibase/uno/loktxdoc.cxx
+++ b/sw/source/uibase/uno/loktxdoc.cxx
@@ -230,6 +230,41 @@ void GetBookmarks(tools::JsonWriter& rJsonWriter, 
SwDocShell* pDocShell,
     }
 }
 
+/// Implements getCommandValues(".uno:Bookmark").
+///
+/// Parameters:
+///
+/// - namePrefix: bookmark name prefix to not return all bookmarks
+void GetBookmark(tools::JsonWriter& rJsonWriter, SwDocShell* pDocShell,
+                 const std::map<OUString, OUString>& rArguments)
+{
+    OUString aNamePrefix;
+    {
+        auto it = rArguments.find("namePrefix");
+        if (it != rArguments.end())
+        {
+            aNamePrefix = it->second;
+        }
+    }
+
+    IDocumentMarkAccess& rIDMA = 
*pDocShell->GetDoc()->getIDocumentMarkAccess();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    SwPosition& rCursor = *pWrtShell->GetCursor()->GetPoint();
+    sw::mark::IMark* pBookmark = rIDMA.getBookmarkFor(rCursor);
+    tools::ScopedJsonWriterNode aBookmark = rJsonWriter.startNode("bookmark");
+    if (!pBookmark)
+    {
+        return;
+    }
+
+    if (!pBookmark->GetName().startsWith(aNamePrefix))
+    {
+        return;
+    }
+
+    rJsonWriter.put("name", pBookmark->GetName());
+}
+
 /// Implements getCommandValues(".uno:Fields").
 ///
 /// Parameters:
@@ -328,8 +363,8 @@ void GetSections(tools::JsonWriter& rJsonWriter, 
SwDocShell* pDocShell,
 bool SwXTextDocument::supportsCommand(const OUString& rCommand)
 {
     static const std::initializer_list<std::u16string_view> vForward
-        = { u"TextFormFields", u"TextFormField", u"SetDocumentProperties",
-            u"Bookmarks",      u"Fields",        u"Sections" };
+        = { u"TextFormFields", u"TextFormField", u"SetDocumentProperties", 
u"Bookmarks", u"Fields",
+            u"Sections",       u"Bookmark" };
 
     return std::find(vForward.begin(), vForward.end(), rCommand) != 
vForward.end();
 }
@@ -344,6 +379,7 @@ void SwXTextDocument::getCommandValues(tools::JsonWriter& 
rJsonWriter, const OSt
     static constexpr OStringLiteral aBookmarks(".uno:Bookmarks");
     static constexpr OStringLiteral aFields(".uno:Fields");
     static constexpr OStringLiteral aSections(".uno:Sections");
+    static constexpr OStringLiteral aBookmark(".uno:Bookmark");
 
     INetURLObject aParser(OUString::fromUtf8(rCommand));
     OUString aArguments = aParser.GetParam();
@@ -391,6 +427,10 @@ void SwXTextDocument::getCommandValues(tools::JsonWriter& 
rJsonWriter, const OSt
     {
         GetSections(rJsonWriter, m_pDocShell, aMap);
     }
+    else if (o3tl::starts_with(rCommand, aBookmark))
+    {
+        GetBookmark(rJsonWriter, m_pDocShell, aMap);
+    }
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to