desktop/source/lib/init.cxx                              |    9 +
 libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx    |    1 
 libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx |   35 +++++
 libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx |    2 
 libreofficekit/qa/gtktiledviewer/gtv.ui                  |   20 ++-
 sw/qa/uibase/uno/uno.cxx                                 |   36 +++++
 sw/source/uibase/uno/unotxdoc.cxx                        |   94 +++++++++++----
 7 files changed, 168 insertions(+), 29 deletions(-)

New commits:
commit 95921862bdecef95faa73501aadf17fd1aa14218
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Nov 30 08:57:50 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Dec 5 08:09:16 2022 +0000

    libreofficekit: add a way to invoke getCommandValues()
    
    - add a new button to the toolbar to invoke the getCommandValues() LOK
      API with user-provided command name (and parameters)
    
    - log the result using g_info(), which is visible on the console if
      gtktiledviewer is started with G_MESSAGES_DEBUG=all
    
    - change some labels to tooltip texts that actually show up on mouse
      hover so you can understand what button does what
    
    - tweak the icons, so the question mark icon is the getter and the info
      icon is the setter
    
    (cherry picked from commit 2c149dc9983317bce9116649270c3513adc35514)
    
    Change-Id: If6984d2dde3d669b42aafcc3f58a0ca757ccaaff
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143494
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx 
b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
index 84c5335b32c2..a89b8fc7fc72 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-main-toolbar.cxx
@@ -129,6 +129,7 @@ gtv_main_toolbar_init(GtvMainToolbar* toolbar)
     gtk_builder_add_callback_symbol(builder.get(), "getRulerState", 
G_CALLBACK(getRulerState));
     gtk_builder_add_callback_symbol(builder.get(), "recentUnoChanged", 
G_CALLBACK(recentUnoChanged));
     gtk_builder_add_callback_symbol(builder.get(), "unoCommandDebugger", 
G_CALLBACK(unoCommandDebugger));
+    gtk_builder_add_callback_symbol(builder.get(), "commandValuesDebugger", 
G_CALLBACK(commandValuesDebugger));
     gtk_builder_add_callback_symbol(builder.get(), "toggleEditing", 
G_CALLBACK(toggleEditing));
     gtk_builder_add_callback_symbol(builder.get(), "changePartMode", 
G_CALLBACK(changePartMode));
     gtk_builder_add_callback_symbol(builder.get(), "changePart", 
G_CALLBACK(changePart));
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx 
b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
index bb0e7edd390d..e1dd0347041a 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.cxx
@@ -284,6 +284,41 @@ void unoCommandDebugger(GtkWidget* pButton, gpointer /* 
pItem */)
     gtk_widget_destroy(pUnoCmdDialog);
 }
 
+void commandValuesDebugger(GtkWidget* pButton, gpointer /* pItem */)
+{
+    GtvApplicationWindow* window = 
GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(pButton));
+    GtkWidget* pUnoCmdDialog = gtk_dialog_new_with_buttons ("Get command 
values",
+                                                            GTK_WINDOW 
(window),
+                                                            GTK_DIALOG_MODAL,
+                                                            "Execute",
+                                                            GTK_RESPONSE_OK,
+                                                            nullptr);
+    g_object_set(G_OBJECT(pUnoCmdDialog), "resizable", FALSE, nullptr);
+    GtkWidget* pDialogMessageArea = gtk_dialog_get_content_area (GTK_DIALOG 
(pUnoCmdDialog));
+    GtkWidget* pUnoCmdAreaBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+    gtk_box_pack_start(GTK_BOX(pDialogMessageArea), pUnoCmdAreaBox, true, 
true, 2);
+
+    GtkWidget* pUnoCmdLabel = gtk_label_new("Enter UNO command");
+    gtk_box_pack_start(GTK_BOX(pUnoCmdAreaBox), pUnoCmdLabel, true, true, 2);
+
+    GtkWidget* pUnoCmdEntry = gtk_entry_new ();
+    gtk_box_pack_start(GTK_BOX(pUnoCmdAreaBox), pUnoCmdEntry, true, true, 2);
+    gtk_entry_set_placeholder_text(GTK_ENTRY(pUnoCmdEntry), "e.g. .uno:Undo");
+
+    gtk_widget_show_all(pUnoCmdDialog);
+
+    gint res = gtk_dialog_run (GTK_DIALOG(pUnoCmdDialog));
+    if (res == GTK_RESPONSE_OK)
+    {
+        const gchar* pUnoCmd = gtk_entry_get_text(GTK_ENTRY(pUnoCmdEntry));
+        gchar* pValues = 
lok_doc_view_get_command_values(LOK_DOC_VIEW(window->lokdocview), pUnoCmd);
+        g_info("lok::Document::getCommandValues(%s) : %s", pUnoCmd, pValues);
+        g_free(pValues);
+    }
+
+    gtk_widget_destroy(pUnoCmdDialog);
+}
+
 void toggleEditing(GtkWidget* pButton, gpointer /*pItem*/)
 {
     GtvApplicationWindow* window = 
GTV_APPLICATION_WINDOW(gtk_widget_get_toplevel(pButton));
diff --git a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx 
b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
index c06017d87414..447b7be8898f 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
+++ b/libreofficekit/qa/gtktiledviewer/gtv-signal-handlers.hxx
@@ -26,6 +26,8 @@ void recentUnoChanged(GtkWidget* pSelector, gpointer /* pItem 
*/);
 
 void unoCommandDebugger(GtkWidget* pButton, gpointer /* pItem */);
 
+void commandValuesDebugger(GtkWidget* pButton, gpointer /* pItem */);
+
 void toggleEditing(GtkWidget* pButton, gpointer /*pItem*/);
 
 void changePartMode(GtkWidget* pSelector, gpointer /* pItem */);
diff --git a/libreofficekit/qa/gtktiledviewer/gtv.ui 
b/libreofficekit/qa/gtktiledviewer/gtv.ui
index 79cb9a9ec409..5c64b3d42455 100644
--- a/libreofficekit/qa/gtktiledviewer/gtv.ui
+++ b/libreofficekit/qa/gtktiledviewer/gtv.ui
@@ -469,7 +469,7 @@
     <child>
       <object class="GtkToggleToolButton" id="btn_editmode">
         <property name="visible">True</property>
-        <property name="label" translatable="yes">Turn on/off edit 
mode</property>
+        <property name="tooltip_text">Turn on/off edit mode</property>
         <property name="use_underline">True</property>
         <property name="icon_name">insert-text-symbolic</property>
         <signal name="clicked" handler="toggleEditing" swapped="no"/>
@@ -492,6 +492,7 @@
     <child>
       <object class="GtkToolItem" id="recentunoselectortoolitem">
         <property name="visible">True</property>
+        <property name="tooltip_text">Recent UNO command selector</property>
         <property name="can_focus">False</property>
         <child>
           <object class="GtkComboBoxText" id="combo_recentunoselector">
@@ -513,9 +514,9 @@
     <child>
       <object class="GtkToolButton" id="btn_unodebugger">
         <property name="visible">True</property>
-        <property name="label" translatable="yes">Uno Command 
Debugger</property>
+        <property name="tooltip_text">Uno Command Debugger</property>
         <property name="use_underline">True</property>
-        <property name="icon_name">dialog-question-symbolic</property>
+        <property name="icon_name">dialog-information-symbolic</property>
         <signal name="clicked" handler="unoCommandDebugger" swapped="no"/>
       </object>
       <packing>
@@ -523,6 +524,19 @@
         <property name="homogeneous">True</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkToolButton" id="btn_commandvaluesdebugger">
+        <property name="visible">True</property>
+        <property name="tooltip_text">Command values Debugger</property>
+        <property name="use_underline">True</property>
+        <property name="icon_name">dialog-question-symbolic</property>
+        <signal name="clicked" handler="commandValuesDebugger" swapped="no"/>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="homogeneous">True</property>
+      </packing>
+    </child>
     <child>
       <object class="GtkSeparatorToolItem" id="separator6">
         <property name="visible">True</property>
commit 64e09def77067896ef4550a023c24ca6ac5df8fa
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Nov 29 16:18:16 2022 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Dec 5 08:09:09 2022 +0000

    sw lok, .uno:SetDocumentProperties: expose value of custom document 
properties
    
    LOK API clients currently have no knowledge about document properties.
    
    Clients like Zotero that want to store custom properties on documents
    need a way to read and write such properties. This commit focuses on the
    reading side.
    
    Add a getter for .uno:SetDocumentProperties that allows filtering for a
    certain prefix, this way the returned value can contain only the
    relevant information.
    
    Rework doc_getCommandValues() and SwXTextDocument::getCommandValues() a
    bit, so adding new getters require less duplication.
    
    (cherry picked from commit 5e8f6dcb8ce00d2d5e35b3cf5654187b3068276c)
    
    Conflicts:
            desktop/source/lib/init.cxx
            sw/qa/uibase/uno/uno.cxx
            sw/source/uibase/uno/unotxdoc.cxx
    
    Change-Id: I0c52cd2efcc8b1ea7307763c8252dd1e8ffdea2d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143493
    Tested-by: Miklos Vajna <vmik...@collabora.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 9fc497d46b0e..1331aac9c69d 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -5725,7 +5725,10 @@ 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");
+    static const std::initializer_list<std::u16string_view> vForward = {
+        u"TextFormFields",
+        u"SetDocumentProperties"
+    };
 
     if (!strcmp(pCommand, ".uno:LanguageStatus"))
     {
@@ -5902,7 +5905,9 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* 
pThis, const char* pCo
     {
         return getFontSubset(std::string_view(pCommand + 
aFontSubset.getLength()));
     }
-    else if (aCommand.startsWith(aTextFormFields))
+    else if (std::find(vForward.begin(), vForward.end(),
+                       
INetURLObject(OUString::fromUtf8(aCommand)).GetURLPath())
+             != vForward.end())
     {
         ITiledRenderable* pDoc = getTiledRenderable(pThis);
         if (!pDoc)
diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx
index 18390d2c03f6..d8bb4dc68fba 100644
--- a/sw/qa/uibase/uno/uno.cxx
+++ b/sw/qa/uibase/uno/uno.cxx
@@ -14,6 +14,7 @@
 #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/beans/PropertyAttribute.hpp>
 
 #include <vcl/scheduler.hxx>
 #include <tools/json_writer.hxx>
@@ -134,12 +135,45 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, 
testGetTextFormFields)
     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:
+    // Without the accompanying fix 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_TEST_FIXTURE(SwUibaseUnoTest, testGetDocumentProperties)
+{
+    // Given a document with 3 custom properties: 2 zotero ones and an other 
one:
+    SwDoc* pDoc = createSwDoc();
+    SwDocShell* pDocShell = pDoc->GetDocShell();
+    uno::Reference<document::XDocumentPropertiesSupplier> 
xDPS(pDocShell->GetModel(),
+                                                               uno::UNO_QUERY);
+    uno::Reference<document::XDocumentProperties> xDP = 
xDPS->getDocumentProperties();
+    uno::Reference<beans::XPropertyContainer> xUDP = 
xDP->getUserDefinedProperties();
+    xUDP->addProperty("ZOTERO_PREF_1", beans::PropertyAttribute::REMOVABLE,
+                      uno::Any(OUString("foo")));
+    xUDP->addProperty("ZOTERO_PREF_2", beans::PropertyAttribute::REMOVABLE,
+                      uno::Any(OUString("bar")));
+    xUDP->addProperty("OTHER", beans::PropertyAttribute::REMOVABLE, 
uno::Any(OUString("baz")));
+
+    // When getting the zotero properties:
+    tools::JsonWriter aJsonWriter;
+    OString aCommand(".uno:SetDocumentProperties?namePrefix=ZOTERO_PREF_");
+    auto pXTextDocument = dynamic_cast<SwXTextDocument*>(mxComponent.get());
+    pXTextDocument->getCommandValues(aJsonWriter, aCommand);
+
+    // Then make sure we find the 2 properties and ignore the other one:
+    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 accompanying fix in place, this test would have failed with:
+    // - No such node (userDefinedProperties)
+    // i.e. the returned JSON was just empty.
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2),
+                         aTree.get_child("userDefinedProperties").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 a13bd7b2ab42..9d7b6f31c51d 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -85,6 +85,7 @@
 #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
 #include <com/sun/star/beans/PropertyAttribute.hpp>
 #include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
 #include <com/sun/star/document/RedlineDisplayType.hpp>
 #include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
 #include <com/sun/star/frame/XController.hpp>
@@ -164,6 +165,7 @@
 
 #include <svx/svdpage.hxx>
 #include <o3tl/string_view.hxx>
+#include <comphelper/sequenceashashmap.hxx>
 
 #include <IDocumentOutlineNodes.hxx>
 #include <SearchResultLocator.hxx>
@@ -3567,6 +3569,48 @@ void GetTextFormFields(tools::JsonWriter& rJsonWriter, 
SwDocShell* pDocShell,
         rJsonWriter.put("command", aCommand);
     }
 }
+
+/// Implements getCommandValues(".uno:SetDocumentProperties").
+///
+/// Parameters:
+///
+/// - namePrefix: field name prefix not not return all user-defined properties
+void GetDocumentProperties(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;
+    }
+
+    uno::Reference<document::XDocumentPropertiesSupplier> 
xDPS(pDocShell->GetModel(), uno::UNO_QUERY);
+    uno::Reference<document::XDocumentProperties> xDP = 
xDPS->getDocumentProperties();
+    uno::Reference<beans::XPropertyAccess> 
xUDP(xDP->getUserDefinedProperties(), uno::UNO_QUERY);
+    auto aUDPs = comphelper::sequenceToContainer< 
std::vector<beans::PropertyValue> >(xUDP->getPropertyValues());
+    tools::ScopedJsonWriterArray aProperties = 
rJsonWriter.startArray("userDefinedProperties");
+    for (const auto& rUDP : aUDPs)
+    {
+        if (!rUDP.Name.startsWith(aNamePrefix))
+        {
+            continue;
+        }
+
+        if (rUDP.Value.getValueTypeClass() != TypeClass_STRING)
+        {
+            continue;
+        }
+
+        OUString aValue;
+        rUDP.Value >>= aValue;
+
+        tools::ScopedJsonWriterStruct aProperty = rJsonWriter.startStruct();
+        rJsonWriter.put("name", rUDP.Name);
+        rJsonWriter.put("type", "string");
+        rJsonWriter.put("value", aValue);
+    }
+}
 }
 
 void SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, const 
OString& rCommand)
@@ -3574,34 +3618,38 @@ void 
SwXTextDocument::getCommandValues(tools::JsonWriter& rJsonWriter, const OSt
     std::map<OUString, OUString> aMap;
 
     static constexpr OStringLiteral aTextFormFields(".uno:TextFormFields");
+    static constexpr OStringLiteral 
aSetDocumentProperties(".uno:SetDocumentProperties");
+
+    INetURLObject aParser(OUString::fromUtf8(rCommand));
+    OUString aArguments = aParser.GetParam();
+    sal_Int32 nParamIndex = 0;
+    do
+    {
+        OUString aParam = aArguments.getToken( 0, '&', nParamIndex );
+        sal_Int32 nIndex = 0;
+        OUString aKey;
+        OUString aValue;
+        do
+        {
+            OUString aToken = aParam.getToken( 0, '=', nIndex );
+            if (aKey.isEmpty())
+                aKey = aToken;
+            else
+                aValue = aToken;
+        } while (nIndex >= 0);
+        OUString aDecodedValue
+            = INetURLObject::decode(aValue, 
INetURLObject::DecodeMechanism::WithCharset);
+        aMap[aKey] = aDecodedValue;
+    } while (nParamIndex >= 0);
 
     if (o3tl::starts_with(rCommand, aTextFormFields))
     {
-        if (rCommand.getLength() > aTextFormFields.getLength())
-        {
-            OString aArguments = rCommand.copy(aTextFormFields.getLength() + 
1);
-            sal_Int32 nParamIndex = 0;
-            do
-            {
-                OString aParamToken = aArguments.getToken(0, '&', nParamIndex);
-                sal_Int32 nIndex = 0;
-                OUString aKey;
-                OUString aValue;
-                do
-                {
-                    OString aToken = aParamToken.getToken(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);
     }
+    if (o3tl::starts_with(rCommand, aSetDocumentProperties))
+    {
+        GetDocumentProperties(rJsonWriter, m_pDocShell, aMap);
+    }
 }
 
 int SwXTextDocument::getPart()

Reply via email to