desktop/qa/desktop_lib/test_desktop_lib.cxx | 23 ++ desktop/source/lib/init.cxx | 44 +++- include/svl/undo.hxx | 6 libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx | 108 +++++++++- libreofficekit/source/gtk/lokdocview.cxx | 2 officecfg/registry/data/org/openoffice/Office/UI/CalcWindowState.xcu | 2 officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu | 2 officecfg/registry/data/org/openoffice/Office/UI/WriterWindowState.xcu | 2 svl/source/undo/undo.cxx | 46 ++++ 9 files changed, 229 insertions(+), 6 deletions(-)
New commits: commit 00a02051b789a12892d3ec3797c853410fbd1fe7 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Fri Aug 5 18:01:06 2016 +0200 gtktiledviewer: expose undo/redo info in a repair document dialog This shows the full undo and redo stack, with all the metadata available via the LOK API. Also fix SfxUndoManager::GetRedoActionsInfo(), so it's easy to show the undo/redo stack in linear time; and fix a use-after-free in lokdocview. Change-Id: I66625ed453efa61b5738d99d7d1ad8f468908240 Reviewed-on: https://gerrit.libreoffice.org/27913 Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> Tested-by: Jenkins <c...@libreoffice.org> (cherry picked from commit a141cba76606c7dcc4965f0a25cc9a4ff2d8879e) diff --git a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx index 034e15f..71572ab 100644 --- a/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx +++ b/libreofficekit/qa/gtktiledviewer/gtktiledviewer.cxx @@ -441,6 +441,106 @@ static void addMoreUnoParam(GtkWidget* /*pWidget*/, gpointer userdata) gtk_widget_show_all(pUnoParamAreaBox); } +static void documentRepair(GtkWidget* pButton, gpointer /*pItem*/) +{ + TiledWindow& rWindow = lcl_getTiledWindow(pButton); + LOKDocView* pDocView = LOK_DOC_VIEW(rWindow.m_pDocView); + // Get the data. + LibreOfficeKitDocument* pDocument = lok_doc_view_get_document(pDocView); + // How it in linear time, so first redo in reverse order, then undo. + std::vector<std::string> aTypes = {".uno:Redo", ".uno:Undo"}; + std::vector<boost::property_tree::ptree> aTrees; + for (size_t nType = 0; nType < aTypes.size(); ++nType) + { + const std::string& rType = aTypes[nType]; + char* pValues = pDocument->pClass->getCommandValues(pDocument, rType.c_str()); + std::stringstream aInfo; + aInfo << "lok::Document::getCommandValues('" << rType << "') returned '" << pValues << "'" << std::endl; + g_info("%s", aInfo.str().c_str()); + std::stringstream aStream(pValues); + free(pValues); + assert(!aStream.str().empty()); + boost::property_tree::ptree aTree; + boost::property_tree::read_json(aStream, aTree); + aTrees.push_back(aTree); + } + + // Create the dialog. + GtkWidget* pDialog = gtk_dialog_new_with_buttons("Repair document", + GTK_WINDOW (gtk_widget_get_toplevel(GTK_WIDGET(pDocView))), + GTK_DIALOG_MODAL, + "Jump to state", + GTK_RESPONSE_OK, + nullptr); + GtkWidget* pContentArea = gtk_dialog_get_content_area(GTK_DIALOG (pDialog)); + + // Build the table. + GtkTreeStore* pTreeStore = gtk_tree_store_new(5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + for (size_t nTree = 0; nTree < aTrees.size(); ++nTree) + { + const auto& rTree = aTrees[nTree]; + for (const auto& rValue : rTree.get_child("actions")) + { + GtkTreeIter aTreeIter; + gtk_tree_store_append(pTreeStore, &aTreeIter, nullptr); + gtk_tree_store_set(pTreeStore, &aTreeIter, + 0, aTypes[nTree].c_str(), + 1, rValue.second.get<int>("index"), + 2, rValue.second.get<std::string>("comment").c_str(), + 3, rValue.second.get<std::string>("viewId").c_str(), + 4, rValue.second.get<std::string>("dateTime").c_str(), + -1); + } + } + GtkWidget* pTreeView = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pTreeStore)); + std::vector<std::string> aColumns = {"Type", "Index", "Comment", "View ID", "Timestamp"}; + for (size_t nColumn = 0; nColumn < aColumns.size(); ++nColumn) + { + GtkCellRenderer* pRenderer = gtk_cell_renderer_text_new(); + GtkTreeViewColumn* pColumn = gtk_tree_view_column_new_with_attributes(aColumns[nColumn].c_str(), + pRenderer, + "text", nColumn, + nullptr); + gtk_tree_view_append_column(GTK_TREE_VIEW(pTreeView), pColumn); + } + gtk_box_pack_start(GTK_BOX(pContentArea), pTreeView, TRUE, TRUE, 2); + + // Show the dialog. + gtk_widget_show_all(pDialog); + gint res = gtk_dialog_run(GTK_DIALOG(pDialog)); + + // Dispatch the matching command, if necessary. + if (res == GTK_RESPONSE_OK) + { + GtkTreeSelection* pSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pTreeView)); + GtkTreeIter aTreeIter; + GtkTreeModel* pTreeModel; + if (gtk_tree_selection_get_selected(pSelection, &pTreeModel, &aTreeIter)) + { + gchar* pType = nullptr; + gint nIndex = 0; + // 0: type, 1: index + gtk_tree_model_get(pTreeModel, &aTreeIter, 0, &pType, 1, &nIndex, -1); + // '.uno:Undo' or '.uno:Redo' + const std::string aType(pType); + // Without the '.uno:' prefix. + std::string aKey = aType.substr(strlen(".uno:")); + g_free(pType); + + // Post the command. + boost::property_tree::ptree aTree; + aTree.put(boost::property_tree::ptree::path_type(aKey + "/type", '/'), "unsigned short"); + aTree.put(boost::property_tree::ptree::path_type(aKey + "/value", '/'), nIndex + 1); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + std::string aArguments = aStream.str(); + lok_doc_view_post_command(pDocView, aType.c_str(), aArguments.c_str(), false); + } + } + + gtk_widget_destroy(pDialog); +} + static void unoCommandDebugger(GtkWidget* pButton, gpointer /* pItem */) { TiledWindow& rWindow = lcl_getTiledWindow(pButton); @@ -1267,7 +1367,7 @@ static GtkWidget* createWindow(TiledWindow& rWindow) gtk_toolbar_insert( GTK_TOOLBAR(pUpperToolbar), gtk_separator_tool_item_new(), -1); - // Undo and redo. + // Undo, redo and document repair. rWindow.m_pUndo = gtk_tool_button_new(nullptr, nullptr); gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(rWindow.m_pUndo), "edit-undo-symbolic"); gtk_tool_item_set_tooltip_text(rWindow.m_pUndo, "Undo"); @@ -1284,6 +1384,12 @@ static GtkWidget* createWindow(TiledWindow& rWindow) lcl_registerToolItem(rWindow, rWindow.m_pRedo, ".uno:Redo"); gtk_widget_set_sensitive(GTK_WIDGET(rWindow.m_pRedo), false); + GtkToolItem* pDocumentRepair = gtk_tool_button_new(nullptr, nullptr); + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(pDocumentRepair), "document-properties"); + gtk_tool_item_set_tooltip_text(pDocumentRepair, "Document repair"); + gtk_toolbar_insert(GTK_TOOLBAR(pUpperToolbar), pDocumentRepair, -1); + g_signal_connect(G_OBJECT(pDocumentRepair), "clicked", G_CALLBACK(documentRepair), nullptr); + gtk_toolbar_insert(GTK_TOOLBAR(pUpperToolbar), gtk_separator_tool_item_new(), -1); // Find. diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx index 2625316..e5ac6c4 100644 --- a/libreofficekit/source/gtk/lokdocview.cxx +++ b/libreofficekit/source/gtk/lokdocview.cxx @@ -432,7 +432,7 @@ LOKPostCommand (LOKDocView* pDocView, GTask* task = g_task_new(pDocView, nullptr, nullptr, nullptr); LOEvent* pLOEvent = new LOEvent(LOK_POST_COMMAND); GError* error = nullptr; - pLOEvent->m_pCommand = pCommand; + pLOEvent->m_pCommand = g_strdup(pCommand); pLOEvent->m_pArguments = g_strdup(pArguments); pLOEvent->m_bNotifyWhenFinished = bNotifyWhenFinished; diff --git a/svl/source/undo/undo.cxx b/svl/source/undo/undo.cxx index 9a240ba..a9c50b6 100644 --- a/svl/source/undo/undo.cxx +++ b/svl/source/undo/undo.cxx @@ -1364,9 +1364,9 @@ OUString SfxUndoManager::GetRedoActionsInfo() const { boost::property_tree::ptree aActions; const SfxUndoArray* pUndoArray = m_xData->pActUndoArray; - for (size_t i = 0; i < GetRedoActionCount(); ++i) + for (size_t i = GetRedoActionCount(); i > 0; --i) { - boost::property_tree::ptree aAction = lcl_ActionToJson(i, pUndoArray->aUndoActions[pUndoArray->nCurUndoAction + i].pAction); + boost::property_tree::ptree aAction = lcl_ActionToJson(i - 1, pUndoArray->aUndoActions[pUndoArray->nCurUndoAction - 1 + i].pAction); aActions.push_back(std::make_pair("", aAction)); } commit e37a16146b2c17d85472fb4ffb18acb5f3ab93aa Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Fri Aug 5 13:53:57 2016 +0200 desktop: add undo/redo support to lok::Document::getCommandValues() Expose the undo/redo stack and the metadata of each item. Reviewed-on: https://gerrit.libreoffice.org/27905 Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> Tested-by: Jenkins <c...@libreoffice.org> (cherry picked from commit 9dd8a0dcfdff21269f6423224d39d168519fb67e) Conflicts: include/svl/undo.hxx Change-Id: I66b81e855a945c97be3d491ed709959f310d4b73 diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index 74a75ed..5a34036 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -79,6 +79,7 @@ public: void testSaveAsCalc(); void testPasteWriter(); void testPasteWriterJPEG(); + void testUndoWriter(); void testRowColumnHeaders(); void testHiddenRowHeaders(); void testCellCursor(); @@ -106,6 +107,7 @@ public: CPPUNIT_TEST(testSaveAsCalc); CPPUNIT_TEST(testPasteWriter); CPPUNIT_TEST(testPasteWriterJPEG); + CPPUNIT_TEST(testUndoWriter); CPPUNIT_TEST(testRowColumnHeaders); CPPUNIT_TEST(testHiddenRowHeaders); CPPUNIT_TEST(testCellCursor); @@ -501,6 +503,27 @@ void DesktopLOKTest::testPasteWriterJPEG() comphelper::LibreOfficeKit::setActive(false); } +void DesktopLOKTest::testUndoWriter() +{ + // Load a Writer document and press a key. + comphelper::LibreOfficeKit::setActive(); + LibLODocument_Impl* pDocument = loadDoc("blank_text.odt"); + pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYINPUT, 't', 0); + pDocument->pClass->postKeyEvent(pDocument, LOK_KEYEVENT_KEYUP, 't', 0); + + // Get undo info. + boost::property_tree::ptree aTree; + char* pJSON = pDocument->m_pDocumentClass->getCommandValues(pDocument, ".uno:Undo"); + std::stringstream aStream(pJSON); + free(pJSON); + CPPUNIT_ASSERT(!aStream.str().empty()); + boost::property_tree::read_json(aStream, aTree); + // Make sure that pressing a key creates exactly one undo action. + CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aTree.get_child("actions").size()); + + comphelper::LibreOfficeKit::setActive(false); +} + void DesktopLOKTest::testRowColumnHeaders() { /* diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx index fe1245e..851fc91 100644 --- a/desktop/source/lib/init.cxx +++ b/desktop/source/lib/init.cxx @@ -81,6 +81,8 @@ #include <unotools/mediadescriptor.hxx> #include <osl/module.hxx> #include <comphelper/sequence.hxx> +#include <sfx2/sfxbasemodel.hxx> +#include <svl/undo.hxx> #include <app.hxx> @@ -88,7 +90,7 @@ // We also need to hackily be able to start the main libreoffice thread: #include "../app/sofficemain.h" #include "../app/officeipcthread.hxx" -#include "../../inc/lib/init.hxx" +#include <lib/init.hxx> #include "lokinteractionhandler.hxx" #include <lokclipboard.hxx> @@ -1792,6 +1794,38 @@ static char* getStyles(LibreOfficeKitDocument* pThis, const char* pCommand) return pJson; } +enum class UndoOrRedo +{ + UNDO, + REDO +}; + +/// Returns the JSON representation of either an undo or a redo stack. +static char* getUndoOrRedo(LibreOfficeKitDocument* pThis, UndoOrRedo eCommand) +{ + LibLODocument_Impl* pDocument = static_cast<LibLODocument_Impl*>(pThis); + + auto pBaseModel = dynamic_cast<SfxBaseModel*>(pDocument->mxComponent.get()); + if (!pBaseModel) + return nullptr; + + SfxObjectShell* pObjectShell = pBaseModel->GetObjectShell(); + if (!pObjectShell) + return nullptr; + + svl::IUndoManager* pUndoManager = pObjectShell->GetUndoManager(); + if (!pUndoManager) + return nullptr; + + OUString aString; + if (eCommand == UndoOrRedo::UNDO) + aString = pUndoManager->GetUndoActionsInfo(); + else + aString = pUndoManager->GetRedoActionsInfo(); + char* pJson = strdup(aString.toUtf8().getStr()); + return pJson; +} + static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCommand) { OString aCommand(pCommand); @@ -1806,6 +1840,14 @@ static char* doc_getCommandValues(LibreOfficeKitDocument* pThis, const char* pCo { return getStyles(pThis, pCommand); } + else if (aCommand == ".uno:Undo") + { + return getUndoOrRedo(pThis, UndoOrRedo::UNDO); + } + else if (aCommand == ".uno:Redo") + { + return getUndoOrRedo(pThis, UndoOrRedo::REDO); + } else if (aCommand.startsWith(aViewRowColumnHeaders)) { ITiledRenderable* pDoc = getTiledRenderable(pThis); diff --git a/include/svl/undo.hxx b/include/svl/undo.hxx index 95684f5..61daea5 100644 --- a/include/svl/undo.hxx +++ b/include/svl/undo.hxx @@ -205,10 +205,14 @@ namespace svl virtual sal_uInt16 GetUndoActionId() const = 0; virtual OUString GetUndoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const = 0; virtual SfxUndoAction* GetUndoAction( size_t nNo=0 ) const = 0; + /// Get info about all undo actions (comment, view shell id, etc.) + virtual OUString GetUndoActionsInfo() const = 0; virtual size_t GetRedoActionCount( bool const i_currentLevel = CurrentLevel ) const = 0; virtual OUString GetRedoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const = 0; virtual SfxUndoAction* GetRedoAction( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const = 0; + /// Get info about all redo actions (comment, view shell id, etc.) + virtual OUString GetRedoActionsInfo() const = 0; virtual bool Undo() = 0; virtual bool Redo() = 0; @@ -324,9 +328,11 @@ public: virtual sal_uInt16 GetUndoActionId() const override; virtual OUString GetUndoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const override; virtual SfxUndoAction* GetUndoAction( size_t nNo=0 ) const override; + OUString GetUndoActionsInfo() const override; virtual size_t GetRedoActionCount( bool const i_currentLevel = CurrentLevel ) const override; virtual OUString GetRedoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const override; virtual SfxUndoAction* GetRedoAction( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const override; + OUString GetRedoActionsInfo() const override; virtual bool Undo() override; virtual bool Redo() override; virtual void Clear() override; diff --git a/svl/source/undo/undo.cxx b/svl/source/undo/undo.cxx index 0fa3867..9a240ba 100644 --- a/svl/source/undo/undo.cxx +++ b/svl/source/undo/undo.cxx @@ -26,6 +26,7 @@ #include <comphelper/flagguard.hxx> #include <tools/diagnose_ex.h> #include <libxml/xmlwriter.h> +#include <boost/property_tree/json_parser.hpp> #include <unotools/datetime.hxx> #include <vector> @@ -1331,6 +1332,51 @@ void SfxUndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const } } +/// Returns a JSON representation of pAction. +boost::property_tree::ptree lcl_ActionToJson(size_t nIndex, SfxUndoAction* pAction) +{ + boost::property_tree::ptree aRet; + aRet.put("index", nIndex); + aRet.put("comment", pAction->GetComment().toUtf8().getStr()); + aRet.put("viewId", pAction->GetViewShellId()); + aRet.put("dateTime", utl::toISO8601(pAction->GetDateTime().GetUNODateTime()).toUtf8().getStr()); + return aRet; +} + +OUString SfxUndoManager::GetUndoActionsInfo() const +{ + boost::property_tree::ptree aActions; + const SfxUndoArray* pUndoArray = m_xData->pActUndoArray; + for (size_t i = 0; i < GetUndoActionCount(); ++i) + { + boost::property_tree::ptree aAction = lcl_ActionToJson(i, pUndoArray->aUndoActions[pUndoArray->nCurUndoAction - 1 - i].pAction); + aActions.push_back(std::make_pair("", aAction)); + } + + boost::property_tree::ptree aTree; + aTree.add_child("actions", aActions); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + return OUString::fromUtf8(aStream.str().c_str()); +} + +OUString SfxUndoManager::GetRedoActionsInfo() const +{ + boost::property_tree::ptree aActions; + const SfxUndoArray* pUndoArray = m_xData->pActUndoArray; + for (size_t i = 0; i < GetRedoActionCount(); ++i) + { + boost::property_tree::ptree aAction = lcl_ActionToJson(i, pUndoArray->aUndoActions[pUndoArray->nCurUndoAction + i].pAction); + aActions.push_back(std::make_pair("", aAction)); + } + + boost::property_tree::ptree aTree; + aTree.add_child("actions", aActions); + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + return OUString::fromUtf8(aStream.str().c_str()); +} + struct SfxListUndoAction::Impl { sal_uInt16 mnId; commit 210072c9adfb2549f639e296d82c5f4b79cb4315 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Fri Aug 5 09:16:27 2016 +0200 tdf#101154 Classification -> TSCP Classification for the toolbar name Change-Id: Iebca1cfcecb1c498c195be919ed9f50e7eb40400 Reviewed-on: https://gerrit.libreoffice.org/27892 Reviewed-by: Miklos Vajna <vmik...@collabora.co.uk> Tested-by: Jenkins <c...@libreoffice.org> (cherry picked from commit adbaad53624bd88c58cd1ee2ecfb7207c9ab59ee) diff --git a/officecfg/registry/data/org/openoffice/Office/UI/CalcWindowState.xcu b/officecfg/registry/data/org/openoffice/Office/UI/CalcWindowState.xcu index 0327ec7..da01946 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/CalcWindowState.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/CalcWindowState.xcu @@ -48,7 +48,7 @@ <value>false</value> </prop> <prop oor:name="UIName" oor:type="xs:string"> - <value xml:lang="en-US">Classification</value> + <value xml:lang="en-US">TSCP Classification</value> </prop> </node> <node oor:name="private:resource/toolbar/graphicobjectbar" oor:op="replace"> diff --git a/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu b/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu index 2ab12d5..83f8be2 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/ImpressWindowState.xcu @@ -419,7 +419,7 @@ <value>false</value> </prop> <prop oor:name="UIName" oor:type="xs:string"> - <value xml:lang="en-US">Classification</value> + <value xml:lang="en-US">TSCP Classification</value> </prop> </node> <node oor:name="private:resource/toolbar/basicshapes" oor:op="replace"> diff --git a/officecfg/registry/data/org/openoffice/Office/UI/WriterWindowState.xcu b/officecfg/registry/data/org/openoffice/Office/UI/WriterWindowState.xcu index a152859..bfeba29 100644 --- a/officecfg/registry/data/org/openoffice/Office/UI/WriterWindowState.xcu +++ b/officecfg/registry/data/org/openoffice/Office/UI/WriterWindowState.xcu @@ -111,7 +111,7 @@ <value>false</value> </prop> <prop oor:name="UIName" oor:type="xs:string"> - <value xml:lang="en-US">Classification</value> + <value xml:lang="en-US">TSCP Classification</value> </prop> </node> <node oor:name="private:resource/toolbar/tableobjectbar" oor:op="replace"> _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits