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()