[Libreoffice-commits] online.git: 8 commits - common/Message.hpp kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp loleaflet/build loleaflet/debug loleaflet/dist loleaflet/main.js loleaflet/src wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/protocol.txt

Pranav Kant pranavk at collabora.co.uk
Wed Oct 25 07:54:31 UTC 2017


 common/Message.hpp                           |    4 
 kit/ChildSession.cpp                         |  100 ++++++--
 kit/ChildSession.hpp                         |   12 -
 kit/Kit.cpp                                  |   65 +++++
 loleaflet/build/deps.js                      |    3 
 loleaflet/debug/document/loleaflet.html      |    2 
 loleaflet/dist/loleaflet.css                 |    7 
 loleaflet/main.js                            |    3 
 loleaflet/src/control/Control.AlertDialog.js |    6 
 loleaflet/src/control/Control.LokDialog.js   |  308 +++++++++++++++++++++++++++
 loleaflet/src/control/Control.Menubar.js     |   16 +
 loleaflet/src/control/Toolbar.js             |   11 
 loleaflet/src/core/Socket.js                 |    2 
 loleaflet/src/layer/tile/TileLayer.js        |   52 ++++
 wsd/ClientSession.cpp                        |   35 ++-
 wsd/ClientSession.hpp                        |    2 
 wsd/DocumentBroker.cpp                       |   44 +++
 wsd/DocumentBroker.hpp                       |    4 
 wsd/protocol.txt                             |    4 
 19 files changed, 643 insertions(+), 37 deletions(-)

New commits:
commit e9e162b35ed67a99b6979c7e611c0cc3c8c0ead2
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Thu Aug 24 17:17:40 2017 +0530

    lokdialog: Mouse/key events, canvas use & child window impl.
    
    Change-Id: If8ae5c284e7166d4a01bd664644134f11197f988

diff --git a/common/Message.hpp b/common/Message.hpp
index 03c1eaa8..bfd38cb7 100644
--- a/common/Message.hpp
+++ b/common/Message.hpp
@@ -131,7 +131,8 @@ private:
         if (_tokens[0] == "tile:" ||
             _tokens[0] == "tilecombine:" ||
             _tokens[0] == "renderfont:" ||
-            _tokens[0] == "dialogpaint:")
+            _tokens[0] == "dialogpaint:" ||
+            _tokens[0] == "dialogchildpaint:")
         {
             return Type::Binary;
         }
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index e66af1e4..c40a6882 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -219,7 +219,10 @@ bool ChildSession::_handleInput(const char *buffer, int length)
                tokens[0] == "paste" ||
                tokens[0] == "insertfile" ||
                tokens[0] == "key" ||
+               tokens[0] == "dialogkey" ||
                tokens[0] == "mouse" ||
+               tokens[0] == "dialogmouse" ||
+               tokens[0] == "dialogchildmouse" ||
                tokens[0] == "uno" ||
                tokens[0] == "selecttext" ||
                tokens[0] == "selectgraphic" ||
@@ -258,11 +261,23 @@ bool ChildSession::_handleInput(const char *buffer, int length)
         }
         else if (tokens[0] == "key")
         {
-            return keyEvent(buffer, length, tokens);
+            return keyEvent(buffer, length, tokens, LokEventTargetEnum::Document);
+        }
+        else if (tokens[0] == "dialogkey")
+        {
+            return keyEvent(buffer, length, tokens, LokEventTargetEnum::Dialog);
         }
         else if (tokens[0] == "mouse")
         {
-            return mouseEvent(buffer, length, tokens);
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::Document);
+        }
+        else if (tokens[0] == "dialogmouse")
+        {
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::Dialog);
+        }
+        else if (tokens[0] == "dialogchildmouse")
+        {
+            return mouseEvent(buffer, length, tokens, LokEventTargetEnum::DialogChild);
         }
         else if (tokens[0] == "uno")
         {
@@ -702,17 +717,28 @@ bool ChildSession::insertFile(const char* /*buffer*/, int /*length*/, const std:
     return true;
 }
 
-bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/,
+                            const std::vector<std::string>& tokens,
+                            const LokEventTargetEnum target)
 {
     int type, charcode, keycode;
-    if (tokens.size() != 4 ||
-        !getTokenKeyword(tokens[1], "type",
+    std::string dialogId;
+    unsigned counter = 1;
+    unsigned minTotal = 4; // cmdname, type, char, key are strictly required
+    if (target == LokEventTargetEnum::Dialog || target == LokEventTargetEnum::DialogChild)
+    {
+        getTokenString(tokens[counter++], "dialogid", dialogId);
+        minTotal++; // other params still necessarily required
+    }
+
+    if (tokens.size() != minTotal ||
+        !getTokenKeyword(tokens[counter++], "type",
                          {{"input", LOK_KEYEVENT_KEYINPUT}, {"up", LOK_KEYEVENT_KEYUP}},
                          type) ||
-        !getTokenInteger(tokens[2], "char", charcode) ||
-        !getTokenInteger(tokens[3], "key", keycode))
+        !getTokenInteger(tokens[counter++], "char", charcode) ||
+        !getTokenInteger(tokens[counter++], "key", keycode))
     {
-        sendTextFrame("error: cmd=key kind=syntax");
+        sendTextFrame("error: cmd=" + std::string(tokens[0]) + "  kind=syntax");
         return false;
     }
 
@@ -733,15 +759,20 @@ bool ChildSession::keyEvent(const char* /*buffer*/, int /*length*/, const std::v
     }
 
     std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
-
-    getLOKitDocument()->setView(_viewId);
-
-    getLOKitDocument()->postKeyEvent(type, charcode, keycode);
+    if (target == LokEventTargetEnum::Document)
+    {
+        getLOKitDocument()->setView(_viewId);
+        getLOKitDocument()->postKeyEvent(type, charcode, keycode);
+    }
+    else if (!dialogId.empty())
+        getLOKitDocument()->postDialogKeyEvent(dialogId.c_str(), type, charcode, keycode);
 
     return true;
 }
 
-bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens)
+bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/,
+                              const std::vector<std::string>& tokens,
+                              const LokEventTargetEnum target)
 {
     int type, x, y, count;
     bool success = true;
@@ -750,38 +781,59 @@ bool ChildSession::mouseEvent(const char* /*buffer*/, int /*length*/, const std:
     int buttons = 1; // left button
     int modifier = 0;
 
-    if (tokens.size() < 5 ||
-        !getTokenKeyword(tokens[1], "type",
+    std::string dialogId;
+    unsigned counter = 1;
+    unsigned minTotal = 5; // cmdname, type, x, y, count are strictly required
+    if (target == LokEventTargetEnum::Dialog || target == LokEventTargetEnum::DialogChild)
+    {
+        getTokenString(tokens[counter++], "dialogid", dialogId);
+        minTotal++; // other params still necessarily required
+    }
+
+    if (tokens.size() < minTotal ||
+        !getTokenKeyword(tokens[counter++], "type",
                          {{"buttondown", LOK_MOUSEEVENT_MOUSEBUTTONDOWN},
                           {"buttonup", LOK_MOUSEEVENT_MOUSEBUTTONUP},
                           {"move", LOK_MOUSEEVENT_MOUSEMOVE}},
                          type) ||
-        !getTokenInteger(tokens[2], "x", x) ||
-        !getTokenInteger(tokens[3], "y", y) ||
-        !getTokenInteger(tokens[4], "count", count))
+        !getTokenInteger(tokens[counter++], "x", x) ||
+        !getTokenInteger(tokens[counter++], "y", y) ||
+        !getTokenInteger(tokens[counter++], "count", count))
     {
         success = false;
     }
 
     // compatibility with older loleaflets
-    if (success && tokens.size() > 5 && !getTokenInteger(tokens[5], "buttons", buttons))
+    if (success && tokens.size() > counter && !getTokenInteger(tokens[counter++], "buttons", buttons))
         success = false;
 
     // compatibility with older loleaflets
-    if (success && tokens.size() > 6 && !getTokenInteger(tokens[6], "modifier", modifier))
+    if (success && tokens.size() > counter && !getTokenInteger(tokens[counter++], "modifier", modifier))
         success = false;
 
     if (!success)
     {
-        sendTextFrame("error: cmd=mouse kind=syntax");
+        sendTextFrame("error: cmd=" +  std::string(tokens[0]) + " kind=syntax");
         return false;
     }
 
     std::unique_lock<std::mutex> lock(_docManager.getDocumentMutex());
 
-    getLOKitDocument()->setView(_viewId);
-
-    getLOKitDocument()->postMouseEvent(type, x, y, count, buttons, modifier);
+    switch (target)
+    {
+    case LokEventTargetEnum::Document:
+        getLOKitDocument()->setView(_viewId);
+        getLOKitDocument()->postMouseEvent(type, x, y, count, buttons, modifier);
+        break;
+    case LokEventTargetEnum::Dialog:
+        getLOKitDocument()->postDialogMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        break;
+    case LokEventTargetEnum::DialogChild:
+        getLOKitDocument()->postDialogChildMouseEvent(dialogId.c_str(), type, x, y, count, buttons, modifier);
+        break;
+    default:
+        assert(false && "Unsupported mouse target type");
+    }
 
     return true;
 }
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index c3af96ec..82c5b541 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -25,6 +25,13 @@
 
 class ChildSession;
 
+enum class LokEventTargetEnum
+{
+    Document,
+    Dialog,
+    DialogChild
+};
+
 /// An abstract interface that defines the
 /// DocumentManager interface and functionality.
 class IDocumentManager
@@ -174,8 +181,9 @@ private:
     bool getTextSelection(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool paste(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool insertFile(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
-    bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool keyEvent(const char* buffer, int length, const std::vector<std::string>& tokens, const LokEventTargetEnum target);
+    bool dialogKeyEvent(const char* buffer, int length, const std::vector<std::string>& tokens);
+    bool mouseEvent(const char* buffer, int length, const std::vector<std::string>& tokens, const LokEventTargetEnum target);
     bool unoCommand(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool selectText(const char* buffer, int length, const std::vector<std::string>& tokens);
     bool selectGraphic(const char* buffer, int length, const std::vector<std::string>& tokens);
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index d1a9b56c..d2ded17f 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -845,7 +845,7 @@ public:
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
-    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws, bool child)
     {
         assert(ws && "Expected a non-null websocket.");
 
@@ -870,14 +870,19 @@ public:
         int nWidth = nCanvasWidth;
         int nHeight = nCanvasHeight;
         Timestamp timestamp;
-        _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        if (child)
+            _loKitDocument->paintActiveFloatingWindow(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        else
+            _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+
         const double area = nWidth * nHeight;
         const auto elapsed = timestamp.elapsed();
-        LOG_TRC("paintDialog for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
+        LOG_TRC((child ? std::string("paintActiveFloatingWindow") : std::string("paintDialog")) +
+                " for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
                 << " and rendered in " << (elapsed/1000.) <<
                 " ms (" << area / elapsed << " MP/s).");
 
-        const std::string response = "dialogpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
+        const std::string response = std::string(child ? "dialogchildpaint:" : "dialogpaint:") + " id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
         std::vector<char> output;
         output.reserve(response.size() + pixmapDataSize);
         output.resize(response.size());
@@ -886,14 +891,11 @@ public:
         // TODO: use png cache for dialogs too
         if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
         {
-            //FIXME: Return error.
-            //sendTextFrame("error: cmd=tile kind=failure");
-
-            LOG_ERR("Failed to encode dialog into PNG.");
+            LOG_ERR("Failed to encode into PNG.");
             return;
         }
 
-        LOG_TRC("Sending render-dialog response (" << output.size() << " bytes) for: " << response);
+        LOG_TRC("Sending response (" << output.size() << " bytes) for: " << response);
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
@@ -1723,7 +1725,11 @@ private:
                 }
                 else if (tokens[0] == "dialog")
                 {
-                    renderDialog(tokens, _ws);
+                    renderDialog(tokens, _ws, false);
+                }
+                else if (tokens[0] == "dialogchild")
+                {
+                    renderDialog(tokens, _ws, true);
                 }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
@@ -2176,7 +2182,7 @@ void lokit_main(const std::string& childRoot,
                         TerminationFlag = true;
                     }
                     else if (tokens[0] == "tile" || tokens[0] == "tilecombine" || tokens[0] == "canceltiles" ||
-                             tokens[0] == "dialog" ||
+                             tokens[0] == "dialog" || tokens[0] == "dialogchild" ||
                              LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                     {
                         if (document)
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index a9cef890..0c0aa179 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -2,11 +2,14 @@
  * L.Control.LokDialog used for displaying LOK dialogs
  */
 
-/* global vex $ */
+/* global vex $ map */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
 		map.on('dialogpaint', this._onDialogPaint, this);
+		map.on('dialogchildpaint', this._onDialogChildPaint, this);
+		map.on('dialogchild', this._onDialogChildMsg, this);
 		map.on('dialog', this._onDialogMsg, this);
+		map.on('opendialog', this._openDialog, this);
 	},
 
 	_dialogs: {},
@@ -15,22 +18,49 @@ L.Control.LokDialog = L.Control.extend({
 		return this._dialogs[dialogId];
 	},
 
+	_transformDialogId: function(dialogId) {
+		var ret = dialogId;
+		if (dialogId === 'SpellingDialog')
+			ret = 'SpellingAndGrammarDialog';
+		else if (dialogId === 'FindReplaceDialog')
+			ret = 'SearchDialog';
+		else if (dialogId === 'AcceptRejectChangesDialog')
+			ret = 'AcceptTrackedChanges';
+		else if (dialogId === 'FieldDialog')
+			ret = 'InsertField';
+		else if (dialogId === 'BibliographyEntryDialog')
+			ret = 'InsertAuthoritiesEntry';
+		else if (dialogId === 'IndexEntryDialog')
+			ret = 'InsertIndexesEntry';
+
+		return ret;
+	},
+
 	_onDialogMsg: function(e) {
+		// FIXME: core sends a different id for many dialogs in callbacks
+		e.dialogId = this._transformDialogId(e.dialogId);
 		if (e.action === 'invalidate') {
-			// FIXME: core sends a different id for spelling dialog in 'invalidate' cb
-			if (e.dialogId === 'SpellingDialog')
-				e.dialogId = 'SpellingAndGrammarDialog';
-
 			// ignore any invalidate callbacks when we have closed the dialog
-			if (this._isOpen(e.dialogId))
+			if (this._isOpen(e.dialogId)) {
 				this._map.sendDialogCommand(e.dialogId);
+			}
+		} else if (e.action === 'close') {
+			this._onDialogClose(e.dialogId);
 		}
 	},
 
-	_openDialog: function(dialogId, width, height) {
-		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
-		    '<img class="lokdialog_content" width="' + width + '" height="' + height + '"></div>';
-		$(document.body).append(content);
+	_openDialog: function(e) {
+		e.dialogId = e.dialogId.replace('.uno:', '');
+		this._dialogs[e.dialogId] = true;
+
+		this._map.sendDialogCommand(e.dialogId);
+	},
+
+	_launchDialog: function(dialogId, width, height) {
+		var canvas = '<div style="padding: 0px; margin: 0px; overflow: hidden;" id="' + dialogId + '">' +
+		    '<canvas tabindex="0" id="' + dialogId + '-canvas" width="' + width + 'px" height="' + height + 'px"></canvas>' +
+		    '</div>';
+		$(document.body).append(canvas);
 		var that = this;
 		$('#' + dialogId).dialog({
 			width: width,
@@ -44,28 +74,232 @@ L.Control.LokDialog = L.Control.extend({
 			}
 		});
 
+		// attach the mouse/key events
+		$('#' + dialogId + '-canvas').on('mousedown', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + '-canvas').on('mouseup', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + '-canvas').on('keyup keypress keydown', function(e) {
+			e.dialogId = dialogId;
+			that._handleDialogKeyEvent(e);
+		});
+
 		this._dialogs[dialogId] = true;
 	},
 
+	_postDialogMouseEvent: function(type, dialogid, x, y, count, buttons, modifier) {
+		if (!dialogid.startsWith('.uno:'))
+			dialogid = '.uno:' + dialogid;
+
+		this._map._socket.sendMessage('dialogmouse dialogid=' + dialogid +  ' type=' + type +
+		                              ' x=' + x + ' y=' + y + ' count=' + count +
+		                              ' buttons=' + buttons + ' modifier=' + modifier);
+	},
+
+	_postDialogKeyboardEvent: function(type, dialogid, charcode, keycode) {
+		this._map._socket.sendMessage('dialogkey dialogid=' + dialogid + ' type=' + type +
+		                              ' char=' + charcode + ' key=' + keycode);
+	},
+
+	_postDialogChildMouseEvent: function(type, dialogid, x, y, count, buttons, modifier) {
+		if (!dialogid.startsWith('.uno:'))
+			dialogid = '.uno:' + dialogid;
+
+		this._map._socket.sendMessage('dialogchildmouse dialogid=' + dialogid +  ' type=' + type +
+		                              ' x=' + x + ' y=' + y + ' count=' + count +
+		                              ' buttons=' + buttons + ' modifier=' + modifier);
+	},
+
+	_handleDialogKeyEvent: function(e) {
+		var docLayer = this._map._docLayer;
+		this.modifier = 0;
+		var shift = e.originalEvent.shiftKey ? this._map['keyboard'].keyModifier.shift : 0;
+		var ctrl = e.originalEvent.ctrlKey ? this._map['keyboard'].keyModifier.ctrl : 0;
+		var alt = e.originalEvent.altKey ? this._map['keyboard'].keyModifier.alt : 0;
+		var cmd = e.originalEvent.metaKey ? this._map['keyboard'].keyModifier.ctrl : 0;
+		var location = e.originalEvent.location;
+		this.modifier = shift | ctrl | alt | cmd;
+
+		var charCode = e.originalEvent.charCode;
+		var keyCode = e.originalEvent.keyCode;
+		var unoKeyCode = this._map['keyboard']._toUNOKeyCode(keyCode);
+
+		if (this.modifier) {
+			unoKeyCode |= this.modifier;
+			if (e.type !== 'keyup') {
+				this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+				return;
+			}
+		}
+
+		if (e.type === 'keydown' && this._map['keyboard'].handleOnKeyDownKeys[keyCode]) {
+			this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+		}
+		else if (e.type === 'keypress' && (!this._map['keyboard'].handleOnKeyDownKeys[keyCode] || charCode !== 0)) {
+			if (charCode === keyCode && charCode !== 13) {
+				keyCode = 0;
+				unoKeyCode = this._map['keyboard']._toUNOKeyCode(keyCode);
+			}
+			this._postDialogKeyboardEvent('input', e.dialogId, charCode, unoKeyCode);
+		}
+		else if (e.type === 'keyup') {
+			this._postDialogKeyboardEvent('up', e.dialogId, charCode, unoKeyCode);
+		}
+	},
+
 	_onDialogClose: function(dialogId) {
 		$('#' + dialogId).remove();
-		this._dialogs[dialogId] = false;
+		delete this._dialogs[dialogId];
 	},
 
-	_paintDialog: function(dialogId, img) {
+	_paintDialog: function(dialogId, imgData) {
 		if (!this._isOpen(dialogId))
 			return;
 
-		$('#' + dialogId + ' > .lokdialog_content').attr('src', img);
+		var img = new Image();
+		var canvas = document.getElementById(dialogId + '-canvas');
+		var ctx = canvas.getContext('2d');
+		img.onload = function() {
+			ctx.drawImage(img, 0, 0);
+		};
+		img.src = imgData;
+	},
+
+	_isSameSize: function(dialogId, newWidth, newHeight) {
+		var ret = false;
+		if (this._isOpen(dialogId))
+		{
+			var oldWidth = $('#' + dialogId + '-canvas').width();
+			var oldHeight = $('#' + dialogId + '-canvas').height();
+			if (oldWidth === newWidth && oldHeight === newHeight)
+				ret = true;
+		}
+
+		return ret;
 	},
 
+	// Binary dialog msg recvd from core
 	_onDialogPaint: function (e) {
 		var dialogId = e.id.replace('.uno:', '');
+		// is our request to open dialog still valid?
+		if (!this._dialogs[dialogId])
+			return;
+
 		if (!this._isOpen(dialogId)) {
-			this._openDialog(dialogId, e.width, e.height);
+			this._launchDialog(dialogId, e.width, e.height);
+		} else if (!this._isSameSize(dialogId, e.width, e.height)) {
+			// size changed - destroy the old sized dialog
+			this._onDialogClose(dialogId);
+			this._launchDialog(dialogId, e.width, e.height);
 		}
 
 		this._paintDialog(dialogId, e.dialog);
+	},
+
+	_onDialogChildPaint: function(e) {
+		var dialogId = e.id.replace('.uno:', '');
+		var img = new Image();
+		var canvas = document.getElementById(dialogId + '-floating');
+		canvas.width = e.width;
+		canvas.height = e.height;
+		var ctx = canvas.getContext('2d');
+		img.onload = function() {
+			ctx.drawImage(img, 0, 0);
+		};
+		img.src = e.dialog;
+	},
+
+	_onDialogChildClose: function(dialogId) {
+		$('#' + dialogId + '-floating').remove();
+	},
+
+	_isDialogChildUnchanged: function(dialogId, left, top) {
+		// get pervious dialog child's specs
+		var oldLeft = $('#' + dialogId + '-floating').css('left');
+		var oldTop = $('#' + dialogId + '-floating').css('top');
+		if (!oldLeft || !oldTop) {
+			// no left or top position set earlier; this is first dialog child placement
+			return false;
+		}
+
+		oldLeft = parseInt(oldLeft);
+		oldTop = parseInt(oldTop);
+		if (oldLeft !== left || oldTop !== top) {
+			// something changed in new dialog child
+			return false;
+		}
+
+		return true;
+	},
+
+	_launchDialogChild: function(e) {
+		var positions = e.position.split(',');
+		var left = parseInt(positions[0]);
+		var top = parseInt(positions[1]);
+		// ignore spurious "0, 0" dialog child position recvd from backend
+		if (e.position === '0, 0' || this._isDialogChildUnchanged(e.dialogId, left, top)) {
+			// ignore
+			return;
+		}
+
+		// remove any existing floating element if there's any
+		$('#' + e.dialogId + '-floating').remove();
+		var floatingCanvas = '<canvas id="' + e.dialogId + '-floating"></canvas>';
+		$('#' + e.dialogId).append(floatingCanvas);
+		$('#' + e.dialogId + '-floating').css({position: 'absolute', left: left, top: top});
+
+		var that = this;
+		var dialogId = e.dialogId;
+		// attach events
+		$('#' + dialogId + '-floating').on('mousedown', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogChildMouseEvent('buttondown', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + '-floating').on('mouseup', function(e) {
+			var buttons = 0;
+			buttons |= e.button === map['mouse'].JSButtons.left ? map['mouse'].LOButtons.left : 0;
+			buttons |= e.button === map['mouse'].JSButtons.middle ? map['mouse'].LOButtons.middle : 0;
+			buttons |= e.button === map['mouse'].JSButtons.right ? map['mouse'].LOButtons.right : 0;
+			var modifier = 0;
+			that._postDialogChildMouseEvent('buttonup', dialogId, e.offsetX, e.offsetY, 1, buttons, modifier);
+		});
+
+		$('#' + dialogId + '-floating').on('mousemove', function(e) {
+			that._postDialogChildMouseEvent('move', dialogId, e.offsetX, e.offsetY, 1, 0, 0);
+		});
+
+	},
+
+	_onDialogChildMsg: function(e) {
+		e.dialogId = this._transformDialogId(e.dialogId);
+		if (e.action === 'invalidate') {
+			if (this._isOpen(e.dialogId))
+			{
+				this._map.sendDialogCommand(e.dialogId, false /* no json */, true /* dialog child*/);
+				this._launchDialogChild(e);
+			}
+		} else if (e.action === 'close') {
+			this._onDialogChildClose(e.dialogId);
+		}
 	}
 });
 
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 0812d745..dfae7d89 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -180,8 +180,15 @@ L.Control.Menubar = L.Control.extend({
 					{name: _('None (Do not check spelling)'), id: 'noneparagraph', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE'}]},
 				{name: _('Language for entire document'), type: 'menu', menu: [
 					{name: _('None (Do not check spelling)'), id: 'nonelanguage', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE'}]},
-				{name: _('Spelling and Grammar'), uno: '.uno:SpellingAndGrammarDialog', type: 'unocommand'},
-				{name: _('Word count'), uno: '.uno:WordCountDialog', type: 'unocommand'}
+				{name: _('Search Dialog'), id: '.uno:SearchDialog', type: 'dialog'},
+				{name: _('Accept/Reject changes'), id: '.uno:AcceptTrackedChanges', type: 'dialog'},
+				{name: _('Insert Field'), id: '.uno:InsertField', type: 'dialog'},
+				{name: _('Image Map Dialog'), id: '.uno:ImageMapDialog', type: 'dialog'},
+				{name: _('Hyperlink Dialog'), id: '.uno:HyperlinkDialog', type: 'dialog'},
+				{name: _('Spelling and Grammar'), id: '.uno:SpellingAndGrammarDialog', type: 'dialog'},
+				{name: _('Insert Indexes Entry'), id: '.uno:InsertIndexesEntry', type: 'dialog'},
+				{name: _('Insert Authority Entry'), id: '.uno:InsertAuthoritiesEntry', type: 'dialog'},
+				{name: _('Word count'), id: '.uno:WordCountDialog', type: 'dialog'}
 			]},
 			{name: _('Help'), id: 'help', type: 'menu', menu: [
 				{name: _('Keyboard shortcuts'), id: 'keyboard-shortcuts', type: 'action'},
@@ -767,7 +774,7 @@ L.Control.Menubar = L.Control.extend({
 		} else if (type === 'action') {
 			self._executeAction(item);
 		} else if (type === 'dialog') {
-			map.sendDialogCommand($(item).data('id'));
+			map.fire('opendialog', {dialogId: $(item).data('id')});
 		}
 
 		if ($(item).data('id') !== 'insertcomment')
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 452f4fc9..274e9190 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -145,11 +145,14 @@ L.Map.include({
 		}
 	},
 
-	sendDialogCommand: function (command, json) {
+	sendDialogCommand: function (command, json, child) {
 		if (this._permission === 'edit') {
 			if (!command.startsWith('.uno:'))
 				command = '.uno:' + command;
-			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
+			var dialogCmd = 'dialog';
+			if (child)
+				dialogCmd = 'dialogchild';
+			this._socket.sendMessage(dialogCmd + ' ' + command + (json ? ' ' + JSON.stringify(json) : ''));
 		}
 	},
 
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index a8e1e442..d83b6afc 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -581,7 +581,7 @@ L.Socket = L.Class.extend({
 				}
 			}
 		}
-		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:')) {
+		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:') && !textMsg.startsWith('dialogchildpaint:')) {
 			// log the tile msg separately as we need the tile coordinates
 			L.Log.log(textMsg, L.INCOMING);
 			if (imgBytes !== undefined) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 1091a8e8..7acb28fb 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -440,8 +440,14 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('dialogpaint:')) {
 			this._onDialogPaintMsg(textMsg, img);
 		}
+		else if (textMsg.startsWith('dialogchildpaint:')) {
+			this._onDialogChildPaintMsg(textMsg, img);
+		}
 		else if (textMsg.startsWith('dialog:')) {
-			this._onDialogMsg(textMsg, img);
+			this._onDialogMsg(textMsg);
+		}
+		else if (textMsg.startsWith('dialogchild:')) {
+			this._onDialogChildMsg(textMsg);
 		}
 		else if (textMsg.startsWith('unocommandresult:')) {
 			this._onUnoCommandResultMsg(textMsg);
@@ -1200,12 +1206,32 @@ L.TileLayer = L.GridLayer.extend({
 		});
 	},
 
+	_onDialogChildPaintMsg: function(textMsg, img) {
+		var command = this._map._socket.parseServerCmd(textMsg);
+		var width = command.width;
+		var height = command.height;
+
+		this._map.fire('dialogchildpaint', {
+			id: command.id,
+			dialog: img,
+			// TODO: add id too
+			width: width,
+			height: height
+		});
+	},
+
 	_onDialogMsg: function(textMsg) {
 		textMsg = textMsg.substring('dialog: '.length);
 		var dialogMsg = JSON.parse(textMsg);
 		this._map.fire('dialog', dialogMsg);
 	},
 
+	_onDialogChildMsg: function(textMsg) {
+		textMsg = textMsg.substring('dialogchild: '.length);
+		var dialogMsg = JSON.parse(textMsg);
+		this._map.fire('dialogchild', dialogMsg);
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 3d917813..e19f3352 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -134,7 +134,10 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "paste" &&
              tokens[0] != "insertfile" &&
              tokens[0] != "key" &&
+             tokens[0] != "dialogkey" &&
              tokens[0] != "mouse" &&
+             tokens[0] != "dialogmouse" &&
+             tokens[0] != "dialogchildmouse" &&
              tokens[0] != "partpagerectangles" &&
              tokens[0] != "ping" &&
              tokens[0] != "renderfont" &&
@@ -153,7 +156,8 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "uno" &&
              tokens[0] != "useractive" &&
              tokens[0] != "userinactive" &&
-             tokens[0] != "dialog")
+             tokens[0] != "dialog" &&
+             tokens[0] != "dialogchild")
     {
         sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
         return false;
@@ -219,7 +223,11 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     }
     else if (tokens[0] == "dialog")
     {
-        return sendDialog(buffer, length, tokens, docBroker);
+        return sendDialog(buffer, length, tokens, docBroker, false);
+    }
+    else if (tokens[0] == "dialogchild")
+    {
+        return sendDialog(buffer, length, tokens, docBroker, true);
     }
     else if (tokens[0] == "tilecombine")
     {
@@ -428,16 +436,20 @@ bool ClientSession::sendTile(const char * /*buffer*/, int /*length*/, const std:
 }
 
 bool ClientSession::sendDialog(const char * /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
-                               const std::shared_ptr<DocumentBroker>& docBroker)
+                               const std::shared_ptr<DocumentBroker>& docBroker, bool child)
 {
+    const std::string dialogCmd = child ? "dialogchild" : "dialog";
     try
     {
-        docBroker->handleDialogRequest(tokens[1], shared_from_this());
+        if (child)
+            docBroker->handleDialogRequest(tokens[1], shared_from_this(), true);
+        else
+            docBroker->handleDialogRequest(tokens[1], shared_from_this(), false);
     }
     catch (const std::exception& exc)
     {
-        LOG_ERR("Failed to process dialog command: " << exc.what());
-        return sendTextFrame("error: cmd=dialog kind=invalid");
+        LOG_ERR("Failed to process " + dialogCmd + " command: " << exc.what());
+        return sendTextFrame("error: cmd=" + dialogCmd + " kind=invalid");
     }
 
     return true;
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 13ce4b1e..b16fe448 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -125,7 +125,7 @@ private:
     bool sendTile(const char* buffer, int length, const std::vector<std::string>& tokens,
                   const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendDialog(const char* buffer, int length, const std::vector<std::string>& tokens,
-                    const std::shared_ptr<DocumentBroker>& docBroker);
+                    const std::shared_ptr<DocumentBroker>& docBroker, bool child);
     bool sendCombinedTiles(const char* buffer, int length, const std::vector<std::string>& tokens,
                            const std::shared_ptr<DocumentBroker>& docBroker);
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index ca1a9504..76255287 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1077,7 +1077,11 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
         }
         else if (command == "dialogpaint:")
         {
-            handleDialogPaintResponse(payload);
+            handleDialogPaintResponse(payload, false);
+        }
+        else if (command == "dialogchildpaint:")
+        {
+            handleDialogPaintResponse(payload, true);
         }
         else if (command == "errortoall:")
         {
@@ -1171,14 +1175,14 @@ void DocumentBroker::handleTileRequest(TileDesc& tile,
 }
 
 void DocumentBroker::handleDialogRequest(const std::string& dialogId,
-                                         const std::shared_ptr<ClientSession>& /*session*/)
+                                         const std::shared_ptr<ClientSession>& /*session*/,
+                                         bool child)
 {
     assertCorrectThread();
     std::unique_lock<std::mutex> lock(_mutex);
-
-    LOG_DBG("Sending dialog render request for dialog " << dialogId);
-    const std::string request = "dialog " + dialogId;
-    _childProcess->sendTextFrame(request);
+    const std::string dialogCmd = child ? "dialogchild" : "dialog";
+    LOG_DBG("Sending " + dialogCmd +  " render request for dialog " << dialogId);
+    _childProcess->sendTextFrame(dialogCmd + " " + dialogId);
 }
 
 void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
@@ -1281,10 +1285,10 @@ void DocumentBroker::handleTileResponse(const std::vector<char>& payload)
     }
 }
 
-void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload)
+void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload, bool child)
 {
     const std::string firstLine = getFirstLine(payload);
-    LOG_DBG("Handling dialogpaint: " << firstLine);
+    LOG_DBG("Handling " << (child ? "dialogchildpaint" : "dialogpaint") << " " << firstLine);
 
     const auto length = payload.size();
     if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 19529797..75e29794 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -299,12 +299,13 @@ public:
     void handleTileRequest(TileDesc& tile,
                            const std::shared_ptr<ClientSession>& session);
     void handleDialogRequest(const std::string& dialogId,
-                             const std::shared_ptr<ClientSession>& session);
+                             const std::shared_ptr<ClientSession>& session,
+                             bool child);
     void handleTileCombinedRequest(TileCombined& tileCombined,
                                    const std::shared_ptr<ClientSession>& session);
     void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
     void handleTileResponse(const std::vector<char>& payload);
-    void handleDialogPaintResponse(const std::vector<char>& payload);
+    void handleDialogPaintResponse(const std::vector<char>& payload, bool child);
     void handleTileCombinedResponse(const std::vector<char>& payload);
 
     void destroyIfLastEditor(const std::string& id);
commit e68dac855e73dc9f1365885edf682ead6166c125
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Aug 23 19:12:48 2017 +0530

    lokdialog: open, close, paint and invalidate the dialog systematically
    
    This makes simple dialog like word count dialog, which do not require
    any key/mouse interaction with the user, work correctly -
    responding to invalidation callbacks and painting it again.
    
    Change-Id: I3eee49751ec7815c8a26a687fc1f3915911d6cd4

diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index b2167be1..a9cef890 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -5,26 +5,67 @@
 /* global vex $ */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
-		// TODO: Better distinction between warnings and errors
+		map.on('dialogpaint', this._onDialogPaint, this);
 		map.on('dialog', this._onDialogMsg, this);
 	},
 
-	_onDialogMsg: function (e) {
-		var dialogId = e.id.replace('.uno:', '');
+	_dialogs: {},
+
+	_isOpen: function(dialogId) {
+		return this._dialogs[dialogId];
+	},
+
+	_onDialogMsg: function(e) {
+		if (e.action === 'invalidate') {
+			// FIXME: core sends a different id for spelling dialog in 'invalidate' cb
+			if (e.dialogId === 'SpellingDialog')
+				e.dialogId = 'SpellingAndGrammarDialog';
+
+			// ignore any invalidate callbacks when we have closed the dialog
+			if (this._isOpen(e.dialogId))
+				this._map.sendDialogCommand(e.dialogId);
+		}
+	},
+
+	_openDialog: function(dialogId, width, height) {
 		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
-		    '<img class="lokdialog_content" src= ' + e.dialog + '></div>';
+		    '<img class="lokdialog_content" width="' + width + '" height="' + height + '"></div>';
 		$(document.body).append(content);
+		var that = this;
 		$('#' + dialogId).dialog({
-			width: e.width,
+			width: width,
 			height: 'auto',
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
 			resizable: false,
-			close: function(e, ui) {
-				$('#' + dialogId).remove();
+			close: function() {
+				that._onDialogClose(dialogId);
 			}
 		});
+
+		this._dialogs[dialogId] = true;
+	},
+
+	_onDialogClose: function(dialogId) {
+		$('#' + dialogId).remove();
+		this._dialogs[dialogId] = false;
+	},
+
+	_paintDialog: function(dialogId, img) {
+		if (!this._isOpen(dialogId))
+			return;
+
+		$('#' + dialogId + ' > .lokdialog_content').attr('src', img);
+	},
+
+	_onDialogPaint: function (e) {
+		var dialogId = e.id.replace('.uno:', '');
+		if (!this._isOpen(dialogId)) {
+			this._openDialog(dialogId, e.width, e.height);
+		}
+
+		this._paintDialog(dialogId, e.dialog);
 	}
 });
 
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index d11b29cd..452f4fc9 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -147,6 +147,8 @@ L.Map.include({
 
 	sendDialogCommand: function (command, json) {
 		if (this._permission === 'edit') {
+			if (!command.startsWith('.uno:'))
+				command = '.uno:' + command;
 			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
 		}
 	},
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index dfb0f33e..1091a8e8 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -438,6 +438,9 @@ L.TileLayer = L.GridLayer.extend({
 			this._onTileMsg(textMsg, img);
 		}
 		else if (textMsg.startsWith('dialogpaint:')) {
+			this._onDialogPaintMsg(textMsg, img);
+		}
+		else if (textMsg.startsWith('dialog:')) {
 			this._onDialogMsg(textMsg, img);
 		}
 		else if (textMsg.startsWith('unocommandresult:')) {
@@ -1183,12 +1186,12 @@ L.TileLayer = L.GridLayer.extend({
 
 	},
 
-	_onDialogMsg: function(textMsg, img) {
+	_onDialogPaintMsg: function(textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var dlgWidth = command.width;
 		var dlgHeight = command.height;
 
-		this._map.fire('dialog', {
+		this._map.fire('dialogpaint', {
 			id: command.id,
 			dialog: img,
 			// TODO: add id too
@@ -1197,6 +1200,12 @@ L.TileLayer = L.GridLayer.extend({
 		});
 	},
 
+	_onDialogMsg: function(textMsg) {
+		textMsg = textMsg.substring('dialog: '.length);
+		var dialogMsg = JSON.parse(textMsg);
+		this._map.fire('dialog', dialogMsg);
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
commit 374d6a197d6f1d9d7de50e77934e08977c930b73
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Aug 23 17:58:23 2017 +0530

    lokdialog: pass the dialog id in dialog command
    
    Change-Id: Ia0e76b1dc93483340361777cf21f23127bac91e1

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 16c68cdd..d1a9b56c 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -877,7 +877,7 @@ public:
                 << " and rendered in " << (elapsed/1000.) <<
                 " ms (" << area / elapsed << " MP/s).");
 
-        const std::string response = "dialogpaint: width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
+        const std::string response = "dialogpaint: id=" + tokens[1] + " width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
         std::vector<char> output;
         output.reserve(response.size() + pixmapDataSize);
         output.resize(response.size());
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index e8a2daf2..b577cafc 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -334,7 +334,7 @@ body {
 	text-indent: 1px;
 }
 
-#lokdialog_container {
+.lokdialog_container.ui-dialog-content.ui-widget-content {
         padding: 0px;
 	overflow: hidden;
 	width: auto;
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index aeb45348..b2167be1 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -10,18 +10,19 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_onDialogMsg: function (e) {
-		var content = '<div id="lokdialog_container"><img id="lokdialog_content" src= ' + e.dialog + '></div>';
+		var dialogId = e.id.replace('.uno:', '');
+		var content = '<div class="lokdialog_container" id="' + dialogId + '">' +
+		    '<img class="lokdialog_content" src= ' + e.dialog + '></div>';
 		$(document.body).append(content);
-		$('#lokdialog_container').dialog({
+		$('#' + dialogId).dialog({
 			width: e.width,
 			height: 'auto',
-			closeText: 'X',
 			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
 			modal: false,
 			closeOnEscape: true,
 			resizable: false,
 			close: function(e, ui) {
-				$('#lokdialog_container').remove();
+				$('#' + dialogId).remove();
 			}
 		});
 	}
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 438d45a9..dfb0f33e 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1189,6 +1189,7 @@ L.TileLayer = L.GridLayer.extend({
 		var dlgHeight = command.height;
 
 		this._map.fire('dialog', {
+			id: command.id,
 			dialog: img,
 			// TODO: add id too
 			width: dlgWidth,
commit e74a4e24871147e3c1c07ad1ff23f72e680c32e6
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Mon Aug 21 15:17:22 2017 +0530

    lokdialog: wrap the lok dialog image in dialog container
    
    The two test dialogs, Spelling dialog and word count dialog
    appear on the screen when invoked from the menubar.
    
    Interaction with dialogs is WIP.
    Mouse/key events and floating window still needs to be worked upon.
    
    Change-Id: I2d439465c7536d0b453c8ade503ec181a96c90d2

diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 1ab853a7..16c68cdd 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -849,7 +849,9 @@ public:
     {
         assert(ws && "Expected a non-null websocket.");
 
-        size_t pixmapDataSize = 4 * 800 * 600;
+        const int nCanvasWidth = 800;
+        const int nCanvasHeight = 600;
+        size_t pixmapDataSize = 4 * nCanvasWidth * nCanvasHeight;
         std::vector<unsigned char> pixmap(pixmapDataSize);
 
         std::unique_lock<std::mutex> lock(_documentMutex);
@@ -865,8 +867,8 @@ public:
             return;
         }
 
-        int nWidth = 0;
-        int nHeight = 0;
+        int nWidth = nCanvasWidth;
+        int nHeight = nCanvasHeight;
         Timestamp timestamp;
         _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
         const double area = nWidth * nHeight;
@@ -882,7 +884,7 @@ public:
         std::memcpy(output.data(), response.data(), response.size());
 
         // TODO: use png cache for dialogs too
-        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nWidth, nHeight, output, LOK_TILEMODE_RGBA))
+        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nCanvasWidth, nCanvasHeight, output, LOK_TILEMODE_RGBA))
         {
             //FIXME: Return error.
             //sendTextFrame("error: cmd=tile kind=failure");
@@ -955,7 +957,7 @@ public:
         const auto mode = static_cast<LibreOfficeKitTileMode>(_loKitDocument->getTileMode());
 
         std::vector<char> output;
-        output.reserve(pixmapWidth * pixmapHeight * 4);
+        output.reserve(pixmapSize);
 
         size_t tileIndex = 0;
         for (Util::Rectangle& tileRect : tileRecs)
diff --git a/loleaflet/dist/loleaflet.css b/loleaflet/dist/loleaflet.css
index a0604c5f..e8a2daf2 100644
--- a/loleaflet/dist/loleaflet.css
+++ b/loleaflet/dist/loleaflet.css
@@ -333,3 +333,10 @@ body {
 	font: bold 18px 'Lucida Console', Monaco, monospace;
 	text-indent: 1px;
 }
+
+#lokdialog_container {
+        padding: 0px;
+	overflow: hidden;
+	width: auto;
+	height: auto;
+}
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
index da592615..aeb45348 100644
--- a/loleaflet/src/control/Control.LokDialog.js
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -2,7 +2,7 @@
  * L.Control.LokDialog used for displaying LOK dialogs
  */
 
-/* global vex */
+/* global vex $ */
 L.Control.LokDialog = L.Control.extend({
 	onAdd: function (map) {
 		// TODO: Better distinction between warnings and errors
@@ -10,7 +10,20 @@ L.Control.LokDialog = L.Control.extend({
 	},
 
 	_onDialogMsg: function (e) {
-		console.log(e);
+		var content = '<div id="lokdialog_container"><img id="lokdialog_content" src= ' + e.dialog + '></div>';
+		$(document.body).append(content);
+		$('#lokdialog_container').dialog({
+			width: e.width,
+			height: 'auto',
+			closeText: 'X',
+			title: 'LOK Dialog', // TODO: Get the 'real' dialog title from the backend
+			modal: false,
+			closeOnEscape: true,
+			resizable: false,
+			close: function(e, ui) {
+				$('#lokdialog_container').remove();
+			}
+		});
 	}
 });
 
commit 99d7ddc92537e655d2ef42bff9d037ae6bd9d95d
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Aug 18 18:45:58 2017 +0530

    lokdialog: loleaflet: infra for receiving lok dialog messages
    
    Change-Id: Id1aa1b31c3f2d4e3a888307d25cd7a8106613156

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 85074949..a3009c56 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -357,6 +357,7 @@ var deps = {
 
 	ControlDialog: {
 		src: ['control/Control.js',
+		      'control/Control.LokDialog.js',
 		      'control/Control.AlertDialog.js'],
 		heading: 'Controls',
 		desc: 'Handles vex dialogs for displaying alerts'
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 8a6a8568..a08e5054 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -126,6 +126,7 @@ global.map = map;
 ////// Controls /////
 map.addControl(L.control.scroll());
 map.addControl(L.control.alertDialog());
+map.addControl(L.control.lokDialog());
 map.addControl(L.control.partsPreview());
 map.addControl(L.control.tabs());
 map.addControl(L.control.columnHeader());
diff --git a/loleaflet/src/control/Control.LokDialog.js b/loleaflet/src/control/Control.LokDialog.js
new file mode 100644
index 00000000..da592615
--- /dev/null
+++ b/loleaflet/src/control/Control.LokDialog.js
@@ -0,0 +1,19 @@
+/*
+ * L.Control.LokDialog used for displaying LOK dialogs
+ */
+
+/* global vex */
+L.Control.LokDialog = L.Control.extend({
+	onAdd: function (map) {
+		// TODO: Better distinction between warnings and errors
+		map.on('dialog', this._onDialogMsg, this);
+	},
+
+	_onDialogMsg: function (e) {
+		console.log(e);
+	}
+});
+
+L.control.lokDialog = function (options) {
+	return new L.Control.LokDialog(options);
+};
diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 80947aa5..0812d745 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -766,6 +766,8 @@ L.Control.Menubar = L.Control.extend({
 			map.sendUnoCommand(unoCommand);
 		} else if (type === 'action') {
 			self._executeAction(item);
+		} else if (type === 'dialog') {
+			map.sendDialogCommand($(item).data('id'));
 		}
 
 		if ($(item).data('id') !== 'insertcomment')
@@ -845,6 +847,9 @@ L.Control.Menubar = L.Control.extend({
 			} else if (menu[i].type === 'unocommand') {
 				$(aItem).data('type', 'unocommand');
 				$(aItem).data('uno', menu[i].uno);
+			} else if (menu[i].type === 'dialog') {
+				$(aItem).data('type', 'dialog');
+				$(aItem).data('id', menu[i].id);
 			} else if (menu[i].type === 'separator') {
 				$(aItem).addClass('separator');
 			} else if (menu[i].type === 'action') {
diff --git a/loleaflet/src/control/Toolbar.js b/loleaflet/src/control/Toolbar.js
index 25693170..d11b29cd 100644
--- a/loleaflet/src/control/Toolbar.js
+++ b/loleaflet/src/control/Toolbar.js
@@ -145,6 +145,12 @@ L.Map.include({
 		}
 	},
 
+	sendDialogCommand: function (command, json) {
+		if (this._permission === 'edit') {
+			this._socket.sendMessage('dialog ' + command + (json ? ' ' + JSON.stringify(json) : ''));
+		}
+	},
+
 	toggleCommandState: function (unoState) {
 		if (this._permission === 'edit') {
 			if (!unoState.startsWith('.uno:')) {
diff --git a/loleaflet/src/core/Socket.js b/loleaflet/src/core/Socket.js
index c0929ffd..a8e1e442 100644
--- a/loleaflet/src/core/Socket.js
+++ b/loleaflet/src/core/Socket.js
@@ -581,7 +581,7 @@ L.Socket = L.Class.extend({
 				}
 			}
 		}
-		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:')) {
+		else if (!textMsg.startsWith('tile:') && !textMsg.startsWith('renderfont:') && !textMsg.startsWith('dialogpaint:')) {
 			// log the tile msg separately as we need the tile coordinates
 			L.Log.log(textMsg, L.INCOMING);
 			if (imgBytes !== undefined) {
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index ca9fbffd..438d45a9 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -437,6 +437,9 @@ L.TileLayer = L.GridLayer.extend({
 		else if (textMsg.startsWith('tile:')) {
 			this._onTileMsg(textMsg, img);
 		}
+		else if (textMsg.startsWith('dialogpaint:')) {
+			this._onDialogMsg(textMsg, img);
+		}
 		else if (textMsg.startsWith('unocommandresult:')) {
 			this._onUnoCommandResultMsg(textMsg);
 		}
@@ -1180,6 +1183,19 @@ L.TileLayer = L.GridLayer.extend({
 
 	},
 
+	_onDialogMsg: function(textMsg, img) {
+		var command = this._map._socket.parseServerCmd(textMsg);
+		var dlgWidth = command.width;
+		var dlgHeight = command.height;
+
+		this._map.fire('dialog', {
+			dialog: img,
+			// TODO: add id too
+			width: dlgWidth,
+			height: dlgHeight
+		});
+	},
+
 	_onTileMsg: function (textMsg, img) {
 		var command = this._map._socket.parseServerCmd(textMsg);
 		var coords = this._twipsToCoords(command);
commit dd3e317a560f8c9b21effd2fdca89c0724fe5fe0
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Aug 18 18:35:51 2017 +0530

    lokdialog: Make space for dialog class
    
    Change-Id: Ic3bccac355069491afdc76db70bea8d5c570e086

diff --git a/loleaflet/build/deps.js b/loleaflet/build/deps.js
index 7044f43d..85074949 100644
--- a/loleaflet/build/deps.js
+++ b/loleaflet/build/deps.js
@@ -357,7 +357,7 @@ var deps = {
 
 	ControlDialog: {
 		src: ['control/Control.js',
-		      'control/Control.Dialog.js'],
+		      'control/Control.AlertDialog.js'],
 		heading: 'Controls',
 		desc: 'Handles vex dialogs for displaying alerts'
 	},
diff --git a/loleaflet/debug/document/loleaflet.html b/loleaflet/debug/document/loleaflet.html
index fc239f3b..2134f31b 100644
--- a/loleaflet/debug/document/loleaflet.html
+++ b/loleaflet/debug/document/loleaflet.html
@@ -131,7 +131,7 @@
 
     ////// Controls /////
     map.addControl(L.control.scroll());
-    map.addControl(L.control.dialog());
+    map.addControl(L.control.alertDialog());
     map.addControl(L.control.partsPreview());
     map.addControl(L.control.tabs());
     map.addControl(L.control.columnHeader());
diff --git a/loleaflet/main.js b/loleaflet/main.js
index 5d8aaed0..8a6a8568 100644
--- a/loleaflet/main.js
+++ b/loleaflet/main.js
@@ -125,7 +125,7 @@ global.map = map;
 
 ////// Controls /////
 map.addControl(L.control.scroll());
-map.addControl(L.control.dialog());
+map.addControl(L.control.alertDialog());
 map.addControl(L.control.partsPreview());
 map.addControl(L.control.tabs());
 map.addControl(L.control.columnHeader());
diff --git a/loleaflet/src/control/Control.Dialog.js b/loleaflet/src/control/Control.AlertDialog.js
similarity index 89%
rename from loleaflet/src/control/Control.Dialog.js
rename to loleaflet/src/control/Control.AlertDialog.js
index daad5e6e..4b937200 100644
--- a/loleaflet/src/control/Control.Dialog.js
+++ b/loleaflet/src/control/Control.AlertDialog.js
@@ -3,7 +3,7 @@
  */
 
 /* global vex */
-L.Control.Dialog = L.Control.extend({
+L.Control.AlertDialog = L.Control.extend({
 	onAdd: function (map) {
 		// TODO: Better distinction between warnings and errors
 		map.on('error', this._onError, this);
@@ -47,6 +47,6 @@ L.Control.Dialog = L.Control.extend({
 	}
 });
 
-L.control.dialog = function (options) {
-	return new L.Control.Dialog(options);
+L.control.alertDialog = function (options) {
+	return new L.Control.AlertDialog(options);
 };
commit de780f82535d9832d48755f9ec8f2dfc517560e4
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Fri Aug 18 18:49:40 2017 +0530

    lokdialog: wsd: first cut at lok dialog rendering api
    
    Change-Id: Id522e0796428ed0a15ea3496e6726f51ed232e75

diff --git a/common/Message.hpp b/common/Message.hpp
index 99ccd77a..03c1eaa8 100644
--- a/common/Message.hpp
+++ b/common/Message.hpp
@@ -130,7 +130,8 @@ private:
     {
         if (_tokens[0] == "tile:" ||
             _tokens[0] == "tilecombine:" ||
-            _tokens[0] == "renderfont:")
+            _tokens[0] == "renderfont:" ||
+            _tokens[0] == "dialogpaint:")
         {
             return Type::Binary;
         }
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 3802ff59..1ab853a7 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -845,6 +845,56 @@ public:
         ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
     }
 
+    void renderDialog(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
+    {
+        assert(ws && "Expected a non-null websocket.");
+
+        size_t pixmapDataSize = 4 * 800 * 600;
+        std::vector<unsigned char> pixmap(pixmapDataSize);
+
+        std::unique_lock<std::mutex> lock(_documentMutex);
+        if (!_loKitDocument)
+        {
+            LOG_ERR("Dialog rendering requested before loading document.");
+            return;
+        }
+
+        if (_loKitDocument->getViewsCount() <= 0)
+        {
+            LOG_ERR("Dialog rendering requested without views.");
+            return;
+        }
+
+        int nWidth = 0;
+        int nHeight = 0;
+        Timestamp timestamp;
+        _loKitDocument->paintDialog(tokens[1].c_str(), pixmap.data(), nWidth, nHeight);
+        const double area = nWidth * nHeight;
+        const auto elapsed = timestamp.elapsed();
+        LOG_TRC("paintDialog for " << tokens[1] << " returned with size" << nWidth << "X" << nHeight
+                << " and rendered in " << (elapsed/1000.) <<
+                " ms (" << area / elapsed << " MP/s).");
+
+        const std::string response = "dialogpaint: width=" + std::to_string(nWidth) + " height=" + std::to_string(nHeight) + "\n";
+        std::vector<char> output;
+        output.reserve(response.size() + pixmapDataSize);
+        output.resize(response.size());
+        std::memcpy(output.data(), response.data(), response.size());
+
+        // TODO: use png cache for dialogs too
+        if (!Png::encodeSubBufferToPNG(pixmap.data(), 0, 0, nWidth, nHeight, nWidth, nHeight, output, LOK_TILEMODE_RGBA))
+        {
+            //FIXME: Return error.
+            //sendTextFrame("error: cmd=tile kind=failure");
+
+            LOG_ERR("Failed to encode dialog into PNG.");
+            return;
+        }
+
+        LOG_TRC("Sending render-dialog response (" << output.size() << " bytes) for: " << response);
+        ws->sendFrame(output.data(), output.size(), WebSocket::FRAME_BINARY);
+    }
+
     void renderCombinedTiles(const std::vector<std::string>& tokens, const std::shared_ptr<LOOLWebSocket>& ws)
     {
         assert(ws && "Expected a non-null websocket.");
@@ -1669,6 +1719,10 @@ private:
                 {
                     renderCombinedTiles(tokens, _ws);
                 }
+                else if (tokens[0] == "dialog")
+                {
+                    renderDialog(tokens, _ws);
+                }
                 else if (LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                 {
                     forwardToChild(tokens[0], input);
@@ -2120,6 +2174,7 @@ void lokit_main(const std::string& childRoot,
                         TerminationFlag = true;
                     }
                     else if (tokens[0] == "tile" || tokens[0] == "tilecombine" || tokens[0] == "canceltiles" ||
+                             tokens[0] == "dialog" ||
                              LOOLProtocol::getFirstToken(tokens[0], '-') == "child")
                     {
                         if (document)
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index cc152e25..3d917813 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -152,7 +152,8 @@ bool ClientSession::_handleInput(const char *buffer, int length)
              tokens[0] != "tilecombine" &&
              tokens[0] != "uno" &&
              tokens[0] != "useractive" &&
-             tokens[0] != "userinactive")
+             tokens[0] != "userinactive" &&
+             tokens[0] != "dialog")
     {
         sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
         return false;
@@ -216,6 +217,10 @@ bool ClientSession::_handleInput(const char *buffer, int length)
     {
         return sendTile(buffer, length, tokens, docBroker);
     }
+    else if (tokens[0] == "dialog")
+    {
+        return sendDialog(buffer, length, tokens, docBroker);
+    }
     else if (tokens[0] == "tilecombine")
     {
         return sendCombinedTiles(buffer, length, tokens, docBroker);
@@ -422,6 +427,22 @@ bool ClientSession::sendTile(const char * /*buffer*/, int /*length*/, const std:
     return true;
 }
 
+bool ClientSession::sendDialog(const char * /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
+                               const std::shared_ptr<DocumentBroker>& docBroker)
+{
+    try
+    {
+        docBroker->handleDialogRequest(tokens[1], shared_from_this());
+    }
+    catch (const std::exception& exc)
+    {
+        LOG_ERR("Failed to process dialog command: " << exc.what());
+        return sendTextFrame("error: cmd=dialog kind=invalid");
+    }
+
+    return true;
+}
+
 bool ClientSession::sendCombinedTiles(const char* /*buffer*/, int /*length*/, const std::vector<std::string>& tokens,
                                       const std::shared_ptr<DocumentBroker>& docBroker)
 {
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index abadb86d..13ce4b1e 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -124,6 +124,8 @@ private:
                           const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendTile(const char* buffer, int length, const std::vector<std::string>& tokens,
                   const std::shared_ptr<DocumentBroker>& docBroker);
+    bool sendDialog(const char* buffer, int length, const std::vector<std::string>& tokens,
+                    const std::shared_ptr<DocumentBroker>& docBroker);
     bool sendCombinedTiles(const char* buffer, int length, const std::vector<std::string>& tokens,
                            const std::shared_ptr<DocumentBroker>& docBroker);
 
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 5025240b..ca1a9504 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1075,6 +1075,10 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
         {
             handleTileCombinedResponse(payload);
         }
+        else if (command == "dialogpaint:")
+        {
+            handleDialogPaintResponse(payload);
+        }
         else if (command == "errortoall:")
         {
             LOG_CHECK_RET(message->tokens().size() == 3, false);
@@ -1166,6 +1170,17 @@ void DocumentBroker::handleTileRequest(TileDesc& tile,
     _debugRenderedTileCount++;
 }
 
+void DocumentBroker::handleDialogRequest(const std::string& dialogId,
+                                         const std::shared_ptr<ClientSession>& /*session*/)
+{
+    assertCorrectThread();
+    std::unique_lock<std::mutex> lock(_mutex);
+
+    LOG_DBG("Sending dialog render request for dialog " << dialogId);
+    const std::string request = "dialog " + dialogId;
+    _childProcess->sendTextFrame(request);
+}
+
 void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
                                                const std::shared_ptr<ClientSession>& session)
 {
@@ -1266,6 +1281,31 @@ void DocumentBroker::handleTileResponse(const std::vector<char>& payload)
     }
 }
 
+void DocumentBroker::handleDialogPaintResponse(const std::vector<char>& payload)
+{
+    const std::string firstLine = getFirstLine(payload);
+    LOG_DBG("Handling dialogpaint: " << firstLine);
+
+    const auto length = payload.size();
+    if (firstLine.size() < static_cast<std::string::size_type>(length) - 1)
+    {
+        const auto buffer = payload.data();
+        const auto offset = firstLine.size() + 1;
+
+        auto msgPayload = std::make_shared<Message>(firstLine,
+                                                    Message::Dir::Out,
+                                                    payload.size());
+        msgPayload->append("\n", 1);
+        msgPayload->append(buffer + offset, payload.size() - offset);
+
+        std::unique_lock<std::mutex> lock(_mutex);
+        for (const auto& sessionIt : _sessions)
+        {
+            sessionIt.second->enqueueSendMessage(msgPayload);
+        }
+    }
+}
+
 void DocumentBroker::handleTileCombinedResponse(const std::vector<char>& payload)
 {
     const std::string firstLine = getFirstLine(payload);
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index 8756e0b5..19529797 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -298,10 +298,13 @@ public:
     void invalidateTiles(const std::string& tiles);
     void handleTileRequest(TileDesc& tile,
                            const std::shared_ptr<ClientSession>& session);
+    void handleDialogRequest(const std::string& dialogId,
+                             const std::shared_ptr<ClientSession>& session);
     void handleTileCombinedRequest(TileCombined& tileCombined,
                                    const std::shared_ptr<ClientSession>& session);
     void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
     void handleTileResponse(const std::vector<char>& payload);
+    void handleDialogPaintResponse(const std::vector<char>& payload);
     void handleTileCombinedResponse(const std::vector<char>& payload);
 
     void destroyIfLastEditor(const std::string& id);
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index 4bd4258d..69224396 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -149,6 +149,10 @@ tilecombine <parameters>
     comma-separated lists, and the number of elements in each must be
     same.
 
+dialog <command>
+
+    <command> is unique identifier for the dialog that needs to be painted.
+
 uno <command>
 
     <command> is a line of text.
commit 0c2fd7ec1d030a2b26d8e3c9332bcf057827fa94
Author: Pranav Kant <pranavk at collabora.co.uk>
Date:   Wed Aug 16 16:54:45 2017 +0530

    lokdialog: Add some dialog commands
    
    Change-Id: Id711292814d9cb61271e898422a740408338593d

diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js
index 91ea05a7..80947aa5 100644
--- a/loleaflet/src/control/Control.Menubar.js
+++ b/loleaflet/src/control/Control.Menubar.js
@@ -179,7 +179,9 @@ L.Control.Menubar = L.Control.extend({
 				{name: _('Language for paragraph'), type: 'menu', menu: [
 					{name: _('None (Do not check spelling)'), id: 'noneparagraph', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE'}]},
 				{name: _('Language for entire document'), type: 'menu', menu: [
-					{name: _('None (Do not check spelling)'), id: 'nonelanguage', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE'}]}
+					{name: _('None (Do not check spelling)'), id: 'nonelanguage', type: 'unocommand', uno: '.uno:LanguageStatus?Language:string=Default_LANGUAGE_NONE'}]},
+				{name: _('Spelling and Grammar'), uno: '.uno:SpellingAndGrammarDialog', type: 'unocommand'},
+				{name: _('Word count'), uno: '.uno:WordCountDialog', type: 'unocommand'}
 			]},
 			{name: _('Help'), id: 'help', type: 'menu', menu: [
 				{name: _('Keyboard shortcuts'), id: 'keyboard-shortcuts', type: 'action'},


More information about the Libreoffice-commits mailing list