From ad1da235d3c7ac893fc7c0d0369b4b7b3ed29db6 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Thu, 8 Feb 2018 00:00:45 +0530 Subject: [PATCH] IME support Dialogs still need to be adapted to this. Only works for documents as of now. Change-Id: I0fb1114e279a9e563943f3f65dd5a577523e9841 --- .../include/LibreOfficeKit/LibreOfficeKit.h | 5 ++ .../include/LibreOfficeKit/LibreOfficeKit.hxx | 11 +++++ .../LibreOfficeKit/LibreOfficeKitEnums.h | 48 +++++++++++++++++-- common/Protocol.cpp | 2 +- kit/ChildSession.cpp | 27 +++++++++++ kit/ChildSession.hpp | 1 + loleaflet/src/map/handler/Map.Keyboard.js | 15 +++--- test/WhiteBoxTests.cpp | 3 ++ wsd/ClientSession.cpp | 1 + 9 files changed, 102 insertions(+), 11 deletions(-) diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/bundled/include/LibreOfficeKit/LibreOfficeKit.h index d42bd3434..ebc112fb7 100644 --- a/bundled/include/LibreOfficeKit/LibreOfficeKit.h +++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.h @@ -300,6 +300,11 @@ struct _LibreOfficeKitDocumentClass /// @see lok::Document::setViewLanguage(). void (*setViewLanguage) (LibreOfficeKitDocument* pThis, int nId, const char* language); + /// @see lok::Document::postExtTextInputEvent + void (*postExtTextInputEvent) (LibreOfficeKitDocument* pThis, + int nType, + const char* pText); + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx index 310b9cfce..098356089 100644 --- a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -537,6 +537,17 @@ public: mpDoc->pClass->setViewLanguage(mpDoc, nId, language); } + /** + * Post the text input from external input window, like IME + * + * @param nType see LibreOfficeKitExtTextInputType + * @param pText Text for LOK_EXT_TEXTINPUT + */ + void postExtTextInputEvent(int nType, const char* pText) + { + mpDoc->pClass->postExtTextInputEvent(mpDoc, nType, pText); + } + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h index f14ce4d36..ccbc3b96b 100644 --- a/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -267,6 +267,11 @@ typedef enum /** * The size and/or the position of the cell cursor changed. * + * Payload format: "x, y, width, height, column, row", where the first + * 4 numbers are document coordinates, in twips, and the last 2 are table + * coordinates starting from 0. + * When the cursor is not shown the payload format is the "EMPTY" string. + * * Rectangle format is the same as LOK_CALLBACK_INVALIDATE_TILES. */ LOK_CALLBACK_CELL_CURSOR = 17, @@ -509,11 +514,12 @@ typedef enum * The column/row header is no more valid because of a column/row insertion * or a similar event. Clients must query a new column/row header set. * - * The payload says if we are invalidating a row or column header. + * The payload says if we are invalidating a row or column header. So, + * payload values can be: "row", "column", "all". */ LOK_CALLBACK_INVALIDATE_HEADER = 33, /** - * The text content of the address field in Calc. + * The text content of the address field in Calc. Eg: "A7" */ LOK_CALLBACK_CELL_ADDRESS = 34, /** @@ -534,7 +540,32 @@ typedef enum */ LOK_CALLBACK_RULER_UPDATE = 35, /** - * Dialog invalidation + * Window related callbacks are emitted under this category. It includes + * external windows like dialogs, autopopups for now. + * + * The payload format is: + * + * { + * "id": "unique integer id of the dialog", + * "action": "", + * "type": "" + * "rectangle": "x, y, width, height" + * } + * + * "type" tells the type of the window the action is associated with + * - "dialog" - window is a dialog + * - "child" - window is a floating window (combo boxes, etc.) + * + * "action" can take following values: + * - "created" - window is created in the backend, client can render it now + * - "title_changed" - window's title is changed + * - "size_changed" - window's size is changed + * - "invalidate" - the area as described by "rectangle" is invalidated + * Clients must request the new area + * - "cursor_invalidate" - cursor is invalidated. New position is in "rectangle" + * - "cursor_visible" - cursor visible status is changed. Status is availabe + * in "visible" field + * - "close" - window is closed */ LOK_CALLBACK_WINDOW = 36, } @@ -549,6 +580,17 @@ typedef enum } LibreOfficeKitKeyEventType; +typedef enum +{ + /// cf. SalEvent::ExtTextInput + LOK_EXT_TEXTINPUT, + /// cf. SalEvent::ExtTextInputPos + LOK_EXT_TEXTINPUT_POS, + /// cf. SalEvent::EndExtTextInput + LOK_EXT_TEXTINPUT_END +} +LibreOfficeKitExtTextInputType; + typedef enum { /// A pressed gesture has started. diff --git a/common/Protocol.cpp b/common/Protocol.cpp index 6ab965e18..9aa4a230a 100644 --- a/common/Protocol.cpp +++ b/common/Protocol.cpp @@ -134,7 +134,7 @@ namespace LOOLProtocol bool getTokenString(const std::string& token, const std::string& name, std::string& value) { - if (token.size() > (name.size() + 1) && + if (token.size() >= (name.size() + 1) && token.compare(0, name.size(), name) == 0 && token[name.size()] == '=') { diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index 9c09e4ba1..efac21b3c 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -227,6 +227,7 @@ bool ChildSession::_handleInput(const char *buffer, int length) tokens[0] == "paste" || tokens[0] == "insertfile" || tokens[0] == "key" || + tokens[0] == "textinput" || tokens[0] == "windowkey" || tokens[0] == "mouse" || tokens[0] == "windowmouse" || @@ -275,6 +276,10 @@ bool ChildSession::_handleInput(const char *buffer, int length) { return keyEvent(buffer, length, tokens, LokEventTargetEnum::Document); } + else if (tokens[0] == "textinput") + { + return extTextInputEvent(buffer, length, tokens); + } else if (tokens[0] == "windowkey") { return keyEvent(buffer, length, tokens, LokEventTargetEnum::Window); @@ -763,6 +768,28 @@ bool ChildSession::insertFile(const char* /*buffer*/, int /*length*/, const std: return true; } +bool ChildSession::extTextInputEvent(const char* /*buffer*/, int /*length*/, + const std::vector& tokens) +{ + int type; + std::string text; + if (tokens.size() < 3 || + !getTokenKeyword(tokens[1], "type", + {{"input", LOK_EXT_TEXTINPUT}, {"end", LOK_EXT_TEXTINPUT_END}}, + type) || + !getTokenString(tokens[2], "text", text)) + { + sendTextFrame("error: cmd=" + std::string(tokens[0]) + " kind=syntax"); + return false; + } + + std::unique_lock lock(_docManager.getDocumentMutex()); + getLOKitDocument()->setView(_viewId); + getLOKitDocument()->postExtTextInputEvent(type, text.c_str()); + + return true; +} + bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::vector& tokens, const LokEventTargetEnum target) diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp index 1c03f5504..093ca4e66 100644 --- a/kit/ChildSession.hpp +++ b/kit/ChildSession.hpp @@ -188,6 +188,7 @@ private: bool paste(const char* buffer, int length, const std::vector& tokens); bool insertFile(const char* buffer, int length, const std::vector& tokens); bool keyEvent(const char* buffer, int length, const std::vector& tokens, const LokEventTargetEnum target); + bool extTextInputEvent(const char* /*buffer*/, int /*length*/, const std::vector& tokens); bool dialogKeyEvent(const char* buffer, int length, const std::vector& tokens); bool mouseEvent(const char* buffer, int length, const std::vector& tokens, const LokEventTargetEnum target); bool unoCommand(const char* buffer, int length, const std::vector& tokens); diff --git a/loleaflet/src/map/handler/Map.Keyboard.js b/loleaflet/src/map/handler/Map.Keyboard.js index 053390a65..a707a7102 100644 --- a/loleaflet/src/map/handler/Map.Keyboard.js +++ b/loleaflet/src/map/handler/Map.Keyboard.js @@ -294,15 +294,18 @@ L.Map.Keyboard = L.Handler.extend({ if (e.type === 'compositionstart' || e.type === 'compositionupdate') { this._isComposing = true; // we are starting composing with IME + var txt = ''; + for (var i = 0; i < e.originalEvent.data.length; i++) { + txt += e.originalEvent.data[i]; + } + if (txt) { + this._map._socket.sendMessage('textinput type=input text=' + txt); + } } if (e.type === 'compositionend') { this._isComposing = false; // stop of composing with IME // get the composited char codes - var compCharCodes = []; - for (var i = 0; i < e.originalEvent.data.length; i++) { - compCharCodes.push(e.originalEvent.data[i].charCodeAt()); - } // clear the input now - best to do this ASAP so the input // is clear for the next word this._map._textArea.value = ''; @@ -350,9 +353,7 @@ L.Map.Keyboard = L.Handler.extend({ } if (e.type === 'compositionend') { // Set all keycodes to zero - for (var idx = 0; i < compCharCodes.length; ++i) { - postEventFn.call(eventObject, 'input', compCharCodes[idx], 0); - } + this._map._socket.sendMessage('textinput type=end text=void'); } else { postEventFn.call(eventObject, 'input', charCode, unoKeyCode); } diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp index 6099839cc..cd4b5ca4b 100644 --- a/test/WhiteBoxTests.cpp +++ b/test/WhiteBoxTests.cpp @@ -58,6 +58,9 @@ void WhiteBoxTests::testLOOLProtocolFunctions() CPPUNIT_ASSERT(LOOLProtocol::getTokenString("bar=hello-sailor", "bar", bar)); CPPUNIT_ASSERT_EQUAL(std::string("hello-sailor"), bar); + CPPUNIT_ASSERT(LOOLProtocol::getTokenString("bar=", "bar", bar)); + CPPUNIT_ASSERT_EQUAL(std::string(""), bar); + int mumble; std::map map { { "hello", 1 }, { "goodbye", 2 }, { "adieu", 3 } }; diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp index 1b98b4ec1..47e2f631b 100644 --- a/wsd/ClientSession.cpp +++ b/wsd/ClientSession.cpp @@ -136,6 +136,7 @@ bool ClientSession::_handleInput(const char *buffer, int length) tokens[0] != "paste" && tokens[0] != "insertfile" && tokens[0] != "key" && + tokens[0] != "textinput" && tokens[0] != "windowkey" && tokens[0] != "mouse" && tokens[0] != "windowmouse" &&