desktop/source/lib/init.cxx       |   14 +++++
 include/vcl/ITiledRenderable.hxx  |    5 ++
 sw/inc/unotxdoc.hxx               |    3 +
 sw/qa/uibase/uno/uno.cxx          |   50 ++++++++++++++++++++
 sw/source/uibase/uno/unotxdoc.cxx |   95 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 167 insertions(+)

New commits:
commit 24219cc1e9829f82a533667aef0f51b6a7df6fc2
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Nov 25 12:30:56 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Nov 25 16:16:39 2022 +0100

    sw lok, .uno:TextFormFields: expose field code of fieldmarks
    
    The fieldmarks in a document were kind of invisible for LOK clients
    previously.
    
    The Zotero use-case requires a way to fetch certain types of fieldmarks
    from a document, to be able to update them and then write back those
    updated versions to the document later.
    
    Fix this by introducing a new .uno:TextFormFields, you can get its value
    using the getCommandValues() LOK API. This allows filtering for a
    certain field command prefix, which is generic, but e.g. in the Zotero
    case allows getting the citations or the bibliography. The returned JSON
    is an array of matching fieldmarks, containing their type and field
    command. It seems there is no way to return the field result, till the
    motivation is to just update that field result. (If there will be need,
    the field result can be added.)
    
    Do this in a way that next time we add a Writer-specific command (to be
    able to return its values), there will be no need to touch
    include/vcl/ITiledRenderable.hxx, causing a large rebuild.
    
    Change-Id: I2ef1159bec4034bbdd6b4ba00715a69423106edd
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143275
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 766248ef00ef..15371581c818 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -5702,6 +5702,7 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* 
pThis, const char* pCo
     static constexpr OStringLiteral 
aSheetGeometryData(".uno:SheetGeometryData");
     static constexpr OStringLiteral aCellCursor(".uno:CellCursor");
     static constexpr OStringLiteral aFontSubset(".uno:FontSubset&name=");
+    static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields");
 
     if (!strcmp(pCommand, ".uno:LanguageStatus"))
     {
@@ -5878,6 +5879,19 @@ static char* 
doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo
     {
         return getFontSubset(aCommand.substr(aFontSubset.getLength()));
     }
+    else if (o3tl::starts_with(aCommand, aTextFormFields))
+    {
+        ITiledRenderable* pDoc = getTiledRenderable(pThis);
+        if (!pDoc)
+        {
+            SetLastExceptionMsg("Document doesn't support tiled rendering");
+            return nullptr;
+        }
+
+        tools::JsonWriter aJsonWriter;
+        pDoc->getCommandValues(aJsonWriter, aCommand);
+        return aJsonWriter.extractData();
+    }
     else
     {
         SetLastExceptionMsg("Unknown command, no values returned");
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index 3bf7bcdddacb..771ecf225c5a 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -368,6 +368,11 @@ public:
      *  Allow / disable drawing current text edit (used in Impress for slide 
previews)
      */
     virtual void setPaintTextEdit(bool) {}
+
+    /// Returns a json mapping of the possible values for the given command.
+    virtual void getCommandValues(tools::JsonWriter& /*rJsonWriter*/, 
std::string_view /*rCommand*/)
+    {
+    }
 };
 } // namespace vcl
 
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 90f605406d5b..150ea15af498 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -459,6 +459,9 @@ public:
     /// @see vcl::ITiledRenderable::executeContentControlEvent().
     void executeContentControlEvent(const StringMap& aArguments) override;
 
+    /// @see vcl::ITiledRenderable::getCommandValues().
+    void getCommandValues(tools::JsonWriter& rJsonWriter, std::string_view 
rCommand) override;
+
     void                        Invalidate();
     void                        Reactivate(SwDocShell* pNewDocShell);
     SwXDocumentPropertyHelper * GetPropertyHelper ();
diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx
index 62af105d24dc..02d95df370c4 100644
--- a/sw/qa/uibase/uno/uno.cxx
+++ b/sw/qa/uibase/uno/uno.cxx
@@ -9,12 +9,17 @@
 
 #include <swmodeltestbase.hxx>
 
+#include <boost/property_tree/json_parser.hpp>
+
 #include <com/sun/star/frame/XModel2.hpp>
 #include <com/sun/star/text/XTextViewTextRangeSupplier.hpp>
 #include <com/sun/star/util/XCloseable.hpp>
 #include <com/sun/star/text/XTextDocument.hpp>
 
 #include <vcl/scheduler.hxx>
+#include <tools/json_writer.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <xmloff/odffields.hxx>
 
 #include <docsh.hxx>
 #include <edtwin.hxx>
@@ -26,6 +31,7 @@
 #include <anchoredobject.hxx>
 #include <frameformats.hxx>
 #include <fmtanchr.hxx>
+#include <unotxdoc.hxx>
 
 /// Covers sw/source/uibase/uno/ fixes.
 class SwUibaseUnoTest : public SwModelTestBase
@@ -151,6 +157,50 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, 
testCreateTextRangeByPixelPositionGraphic)
     CPPUNIT_ASSERT_EQUAL(aAnchorPos, *aPaM.GetPoint());
 }
 
+CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testGetTextFormFields)
+{
+    // Given a document with 3 fieldmarks: 2 zotero items and a zotero
+    // bibliography:
+    createSwDoc();
+    for (int i = 0; i < 2; ++i)
+    {
+        uno::Sequence<css::beans::PropertyValue> aArgs = {
+            comphelper::makePropertyValue("FieldType", 
uno::Any(OUString(ODF_UNHANDLED))),
+            comphelper::makePropertyValue("FieldCommand",
+                                          uno::Any(OUString("ADDIN ZOTERO_ITEM 
foo bar"))),
+            comphelper::makePropertyValue("FieldResult", 
uno::Any(OUString("result"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+    {
+        uno::Sequence<css::beans::PropertyValue> aArgs = {
+            comphelper::makePropertyValue("FieldType", 
uno::Any(OUString(ODF_UNHANDLED))),
+            comphelper::makePropertyValue("FieldCommand",
+                                          uno::Any(OUString("ADDIN ZOTERO_BIBL 
foo bar"))),
+            comphelper::makePropertyValue("FieldResult",
+                                          
uno::Any(OUString("<p>aaa</p><p>bbb</p>"))),
+        };
+        dispatchCommand(mxComponent, ".uno:TextFormField", aArgs);
+    }
+
+    // When getting the zotero items:
+    tools::JsonWriter aJsonWriter;
+    std::string_view 
aCommand(".uno:TextFormFields?type=vnd.oasis.opendocument.field.UNHANDLED&"
+                              "commandPrefix=ADDIN%20ZOTERO_ITEM");
+    auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    pXTextDocument->getCommandValues(aJsonWriter, aCommand);
+
+    // Then make sure we find the 2 items and ignore the bibliography:
+    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);
+    // Without the needed PixelToLogic() call in place, this test would have 
failed with:
+    // - No such node (fields)
+    // i.e. the returned JSON was just empty.
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), 
aTree.get_child("fields").count(""));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/uibase/uno/unotxdoc.cxx 
b/sw/source/uibase/uno/unotxdoc.cxx
index bb6b32723129..62ec394fe47c 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -162,6 +162,7 @@
 #include <tools/UnitConversion.hxx>
 
 #include <svx/svdpage.hxx>
+#include <o3tl/string_view.hxx>
 
 #include <IDocumentOutlineNodes.hxx>
 #include <SearchResultLocator.hxx>
@@ -3507,6 +3508,100 @@ void SwXTextDocument::executeContentControlEvent(const 
StringMap& rArguments)
     }
 }
 
+namespace
+{
+/// Implements getCommandValues(".uno:TextFormFields").
+///
+/// Parameters:
+///
+/// - type: e.g. ODF_UNHANDLED
+/// - commandPrefix: field comment prefix not not return all fieldmarks
+void GetTextFormFields(tools::JsonWriter& rJsonWriter, SwDocShell* pDocShell,
+                       const std::map<OUString, OUString>& rArguments)
+{
+    OUString aType;
+    OUString aCommandPrefix;
+    {
+        auto it = rArguments.find("type");
+        if (it != rArguments.end())
+        {
+            aType = it->second;
+        }
+
+        it = rArguments.find("commandPrefix");
+        if (it != rArguments.end())
+        {
+            aCommandPrefix = it->second;
+        }
+    }
+
+    SwDoc* pDoc = pDocShell->GetDoc();
+    IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+    tools::ScopedJsonWriterArray aFields = rJsonWriter.startArray("fields");
+    for (auto it = pMarkAccess->getFieldmarksBegin(); it != 
pMarkAccess->getFieldmarksEnd(); ++it)
+    {
+        auto pFieldmark = dynamic_cast<sw::mark::IFieldmark*>(*it);
+        assert(pFieldmark);
+        if (pFieldmark->GetFieldname() != aType)
+        {
+            continue;
+        }
+
+        auto itParam = pFieldmark->GetParameters()->find(ODF_CODE_PARAM);
+        if (itParam == pFieldmark->GetParameters()->end())
+        {
+            continue;
+        }
+
+        OUString aCommand;
+        itParam->second >>= aCommand;
+        if (!aCommand.startsWith(aCommandPrefix))
+        {
+            continue;
+        }
+
+        tools::ScopedJsonWriterStruct aField = rJsonWriter.startStruct();
+        rJsonWriter.put("type", aType);
+        rJsonWriter.put("command", aCommand);
+    }
+}
+}
+
+void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, 
std::string_view rCommand)
+{
+    std::map<OUString, OUString> aMap;
+
+    static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields");
+
+    if (o3tl::starts_with(rCommand, aTextFormFields))
+    {
+        if (rCommand.size() > o3tl::make_unsigned(aTextFormFields.getLength()))
+        {
+            std::string_view aArguments = 
rCommand.substr(aTextFormFields.getLength() + 1);
+            sal_Int32 nParamIndex = 0;
+            do
+            {
+                std::string_view aParamToken = o3tl::getToken(aArguments, 0, 
'&', nParamIndex);
+                sal_Int32 nIndex = 0;
+                OUString aKey;
+                OUString aValue;
+                do
+                {
+                    std::string_view aToken = o3tl::getToken(aParamToken, 0, 
'=', nIndex);
+                    if (aKey.isEmpty())
+                        aKey = OUString::fromUtf8(aToken);
+                    else
+                        aValue = OUString::fromUtf8(aToken);
+                } while (nIndex >= 0);
+                OUString aDecodedValue
+                    = INetURLObject::decode(aValue, 
INetURLObject::DecodeMechanism::WithCharset);
+                aMap[aKey] = aDecodedValue;
+            } while (nParamIndex >= 0);
+        }
+        GetTextFormFields(rJsonWriter, m_pDocShell, aMap);
+    }
+}
+
 int SwXTextDocument::getPart()
 {
     SolarMutexGuard aGuard;

Reply via email to