loleaflet/Makefile | 2 loleaflet/src/control/Parts.js | 4 loleaflet/src/layer/marker/Cursor.js | 33 + loleaflet/src/layer/tile/TileLayer.js | 191 +++++++---- loleaflet/src/map/Map.js | 4 loolwsd/ChildSession.cpp | 1 loolwsd/DocumentBroker.cpp | 29 - loolwsd/LOOLKit.cpp | 33 - loolwsd/LOOLStress.cpp | 5 loolwsd/LibreOfficeKit.hpp | 11 loolwsd/MessageQueue.cpp | 2 loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h | 4 loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h | 2 loolwsd/configure.ac | 2 loolwsd/test/data/viewcursor.odp |binary loolwsd/test/helpers.hpp | 1 loolwsd/test/httpwstest.cpp | 165 +++++++++ 17 files changed, 372 insertions(+), 117 deletions(-)
New commits: commit b63c9a6ee45fa24b50e3072f6482d0091bc0e470 Author: Andras Timar <andras.ti...@collabora.com> Date: Wed Sep 14 22:04:14 2016 +0200 Bump version to 1.9.1 diff --git a/loleaflet/Makefile b/loleaflet/Makefile index d249a5a..1e1d7f1 100644 --- a/loleaflet/Makefile +++ b/loleaflet/Makefile @@ -3,7 +3,7 @@ # ("micro") part: Between releases odd, even for releases (no other # changes inbetween). -VERSION=1.9.0 +VERSION=1.9.1 # Version number of the bundled 'draw' thing DRAW_VERSION=0.2.4 diff --git a/loolwsd/configure.ac b/loolwsd/configure.ac index f301f60..06b511c 100644 --- a/loolwsd/configure.ac +++ b/loolwsd/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.69]) -AC_INIT([loolwsd], [1.9.0], [libreoffice@lists.freedesktop.org]) +AC_INIT([loolwsd], [1.9.1], [libreoffice@lists.freedesktop.org]) LT_INIT([shared, disable-static, dlopen]) AM_INIT_AUTOMAKE([1.11 silent-rules subdir-objects]) commit 40c5b26cb2839dbfb3d63ce9c8539767bfe0ce6e Author: Pranav Kant <pran...@collabora.co.uk> Date: Thu Sep 15 00:12:17 2016 +0530 loleaflet: Handle multiview text selections for part documents Change-Id: I56425cde49b2b3b319204d7172f4ec4080b5bed1 (cherry picked from commit 8bd7d83bdcaa2d9654633954f43695a53e07be91) diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js index 55e9dbb..cdde4d1 100644 --- a/loleaflet/src/control/Parts.js +++ b/loleaflet/src/control/Parts.js @@ -34,6 +34,7 @@ L.Map.include({ docLayer.eachView(docLayer._viewCursors, docLayer._onUpdateViewCursor, docLayer); docLayer.eachView(docLayer._cellViewCursors, docLayer._onUpdateCellViewCursor, docLayer); docLayer.eachView(docLayer._graphicViewMarkers, docLayer._onUpdateGraphicViewSelection, docLayer); + docLayer.eachView(docLayer._viewSelections, docLayer._onUpdateTextViewSelection, docLayer); docLayer._clearSelections(); docLayer._updateOnChangePart(); docLayer._pruneTiles(); diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 030b4b4..e6e2c7c 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -719,8 +719,8 @@ L.TileLayer = L.GridLayer.extend({ } // Remove selection, if any. - if (this._viewSelections[viewId]) { - this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId]); + if (this._viewSelections[viewId] && this._viewSelections[viewId].selection) { + this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId].selection); } // Remove the view and update (to refresh as needed). @@ -914,6 +914,7 @@ L.TileLayer = L.GridLayer.extend({ textMsg = textMsg.substring('textviewselection:'.length + 1); var obj = JSON.parse(textMsg); var viewId = parseInt(obj.viewId); + var viewPart = parseInt(obj.part); // Ignore if viewid is same as ours if (viewId === this._viewId) { @@ -921,9 +922,7 @@ L.TileLayer = L.GridLayer.extend({ } var strTwips = obj.selection.match(/\d+/g); - if (this._viewSelections[viewId]) { - this._viewSelectionsGroup.removeLayer(this._viewSelections[viewId]); - } + this._viewSelections[viewId] = this._viewSelections[viewId] || {}; if (strTwips != null) { var rectangles = []; for (var i = 0; i < strTwips.length; i += 4) { @@ -935,16 +934,13 @@ L.TileLayer = L.GridLayer.extend({ rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]); } - var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this); - var selection = new L.Polygon(polygons, { - pointerEvents: 'none', - fillColor: L.LOUtil.getViewIdHexColor(viewId), - fillOpacity: 0.25, - weight: 2, - opacity: 0.25}); - this._viewSelections[viewId] = selection; - this._viewSelectionsGroup.addLayer(selection); + this._viewSelections[viewId].part = viewPart; + this._viewSelections[viewId].polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this); + } else { + this._viewSelections[viewId].polygons = null; } + + this._onUpdateTextViewSelection(viewId); }, _onTextSelectionContentMsg: function (textMsg) { @@ -1177,6 +1173,35 @@ L.TileLayer = L.GridLayer.extend({ } }, + _onUpdateTextViewSelection: function (viewId) { + viewId = parseInt(viewId); + var viewPolygons = this._viewSelections[viewId].polygons; + var viewSelection = this._viewSelections[viewId].selection; + var viewPart = this._viewSelections[viewId].part; + + if (viewPolygons && + (this._docType === 'text' || this._selectedPart === viewPart)) { + + // Reset previous selections + if (viewSelection) { + this._viewSelectionsGroup.removeLayer(viewSelection); + } + + viewSelection = new L.Polygon(viewPolygons, { + pointerEvents: 'none', + fillColor: L.LOUtil.getViewIdHexColor(viewId), + fillOpacity: 0.25, + weight: 2, + opacity: 0.25 + }); + this._viewSelections[viewId].selection = viewSelection; + this._viewSelectionsGroup.addLayer(viewSelection); + } + else if (viewSelection) { + this._viewSelectionsGroup.removeLayer(viewSelection); + } + }, + _onUpdateGraphicViewSelection: function (viewId) { var viewBounds = this._graphicViewMarkers[viewId].bounds; var viewMarker = this._graphicViewMarkers[viewId].marker; commit 0b7c5a8f4cf8369dd3677f5ddea469667de6944c Author: Pranav Kant <pran...@collabora.co.uk> Date: Wed Sep 14 20:07:48 2016 +0530 loleaflet: Remove unused code Change-Id: Ieb12e03b68eaacb99acb9853d02191c165f5b70c (cherry picked from commit 4b18ca3772449d4f8ebd757b01a0efbfcb5c471a) diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index bcb2efe..030b4b4 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -884,7 +884,6 @@ L.TileLayer = L.GridLayer.extend({ this._selections.clearLayers(); if (strTwips != null) { var rectangles = []; - var selectionCenter = new L.Point(0, 0); for (var i = 0; i < strTwips.length; i += 4) { var topLeftTwips = new L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1])); var offset = new L.Point(parseInt(strTwips[i + 2]), parseInt(strTwips[i + 3])); @@ -892,8 +891,6 @@ L.TileLayer = L.GridLayer.extend({ var bottomLeftTwips = topLeftTwips.add(new L.Point(0, offset.y)); var bottomRightTwips = topLeftTwips.add(offset); rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]); - selectionCenter = selectionCenter.add(topLeftTwips); - selectionCenter = selectionCenter.add(offset.divideBy(2)); } var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this); @@ -929,7 +926,6 @@ L.TileLayer = L.GridLayer.extend({ } if (strTwips != null) { var rectangles = []; - var selectionCenter = new L.Point(0, 0); for (var i = 0; i < strTwips.length; i += 4) { var topLeftTwips = new L.Point(parseInt(strTwips[i]), parseInt(strTwips[i + 1])); var offset = new L.Point(parseInt(strTwips[i + 2]), parseInt(strTwips[i + 3])); @@ -937,8 +933,6 @@ L.TileLayer = L.GridLayer.extend({ var bottomLeftTwips = topLeftTwips.add(new L.Point(0, offset.y)); var bottomRightTwips = topLeftTwips.add(offset); rectangles.push([bottomLeftTwips, bottomRightTwips, topLeftTwips, topRightTwips]); - selectionCenter = selectionCenter.add(topLeftTwips); - selectionCenter = selectionCenter.add(offset.divideBy(2)); } var polygons = L.PolyUtil.rectanglesToPolygons(rectangles, this); commit 77a153adf861cf5c7f1405465c3438577bd5a716 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Wed Sep 14 09:20:35 2016 +0200 LOOLStress: unused using declaration Change-Id: If20c38a4575074e7ca30069115002aadd1a1847f (cherry picked from commit a652a013aac06a848c9c7b82671b43d7be5cbbc1) diff --git a/loolwsd/LOOLStress.cpp b/loolwsd/LOOLStress.cpp index 8290298..a296571 100644 --- a/loolwsd/LOOLStress.cpp +++ b/loolwsd/LOOLStress.cpp @@ -66,7 +66,6 @@ protected: using namespace LOOLProtocol; -using Poco::Net::HTTPClientSession; using Poco::Net::HTTPRequest; using Poco::Net::HTTPResponse; using Poco::Runnable; @@ -503,12 +502,12 @@ int Stress::main(const std::vector<std::string>& args) std::cout << "Tile best: " << renderingStats[0] << " microsecs, rendering 95th percentile: " << percentile(renderingStats, 95) << " microsecs." << std::endl; std::cout << "Cached best: " << cachedStats[0] << " microsecs, tile 95th percentile: " << percentile(cachedStats, 95) << " microsecs." << std::endl; - const auto renderingTime = std::accumulate(renderingStats.begin(), renderingStats.end(), 0); + const auto renderingTime = std::accumulate(renderingStats.begin(), renderingStats.end(), 0L); const double renderedPixels = 256 * 256 * renderingStats.size(); const auto pixelsPerSecRendered = renderedPixels / renderingTime; std::cout << "Rendering power: " << pixelsPerSecRendered << " MPixels/sec." << std::endl; - const auto cacheTime = std::accumulate(cachedStats.begin(), cachedStats.end(), 0); + const auto cacheTime = std::accumulate(cachedStats.begin(), cachedStats.end(), 0L); const double cachePixels = 256 * 256 * cachedStats.size(); const auto pixelsPerSecCached = cachePixels / cacheTime; std::cout << "Cache power: " << pixelsPerSecCached << " MPixels/sec." << std::endl; commit 33225f2ae5c468f72496a3a3ee2d7e8776a4df00 Author: Henry Castro <hcas...@collabora.com> Date: Tue Sep 13 17:26:41 2016 -0400 loleaflet: simplify iteration view (cherry picked from commit f85e6cfad9759ec6f4f013e2a0a5ea78f40278fb) diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js index f6c9a18..55e9dbb 100644 --- a/loleaflet/src/control/Parts.js +++ b/loleaflet/src/control/Parts.js @@ -31,9 +31,9 @@ L.Map.include({ docType: docLayer._docType }); this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart); - docLayer._updateViewCursors(); - docLayer._updateCellViewCursors(); - docLayer._updateGraphicViewSelections(); + docLayer.eachView(docLayer._viewCursors, docLayer._onUpdateViewCursor, docLayer); + docLayer.eachView(docLayer._cellViewCursors, docLayer._onUpdateCellViewCursor, docLayer); + docLayer.eachView(docLayer._graphicViewMarkers, docLayer._onUpdateGraphicViewSelection, docLayer); docLayer._clearSelections(); docLayer._updateOnChangePart(); docLayer._pruneTiles(); diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index e9594ae..bcb2efe 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -1208,21 +1208,9 @@ L.TileLayer = L.GridLayer.extend({ } }, - _updateViewCursors: function () { - for (var key in this._viewCursors) { - this._onUpdateViewCursor(key); - } - }, - - _updateCellViewCursors: function () { - for (var key in this._cellViewCursors) { - this._onUpdateCellViewCursor(key); - } - }, - - _updateGraphicViewSelections: function () { - for (var key in this._graphicViewMarkers) { - this._onUpdateGraphicViewSelection(key); + eachView: function (view, method, context) { + for (var key in view) { + method.call(context, key); } }, commit 4980bdb7a9cec1d803c73f40915b2a786c849dd1 Author: Henry Castro <hcas...@collabora.com> Date: Tue Sep 13 16:18:00 2016 -0400 loleaflet: update graphic markers LayerGroup handle internal list Map handle internal list TileLayer handle internal list Reduce internal list (cherry picked from commit 663f47b858749688588cc399c16ecfe126618f50) diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js index 45fd68d..f6c9a18 100644 --- a/loleaflet/src/control/Parts.js +++ b/loleaflet/src/control/Parts.js @@ -33,6 +33,7 @@ L.Map.include({ this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart); docLayer._updateViewCursors(); docLayer._updateCellViewCursors(); + docLayer._updateGraphicViewSelections(); docLayer._clearSelections(); docLayer._updateOnChangePart(); docLayer._pruneTiles(); diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 8b40da6..e9594ae 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -146,10 +146,6 @@ L.TileLayer = L.GridLayer.extend({ map.addLayer(this._viewSelectionsGroup); this._viewSelections = {}; - this._graphicViewMarkersGroup = new L.LayerGroup(); - map.addLayer(this._graphicViewMarkersGroup); - this._graphicViewMarkers = {}; - this._searchResultsLayer = new L.LayerGroup(); map.addLayer(this._searchResultsLayer); @@ -500,23 +496,21 @@ L.TileLayer = L.GridLayer.extend({ } var strTwips = obj.selection.match(/\d+/g); - if (this._graphicViewMarkers[viewId]) { - this._graphicViewMarkersGroup.removeLayer(this._graphicViewMarkers[viewId]); - } + this._graphicViewMarkers[viewId] = this._graphicViewMarkers[viewId] || {}; + this._graphicViewMarkers[viewId].part = parseInt(obj.part); if (strTwips != null) { var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1])); var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3])); var bottomRightTwips = topLeftTwips.add(offset); - var graphicSelection = new L.LatLngBounds( + this._graphicViewMarkers[viewId].bounds = new L.LatLngBounds( this._twipsToLatLng(topLeftTwips, this._map.getZoom()), this._twipsToLatLng(bottomRightTwips, this._map.getZoom())); - this._graphicViewMarkers[viewId] = L.rectangle(graphicSelection, { - pointerEvents: 'none', - fill: false, - color: L.LOUtil.getViewIdHexColor(viewId) - }); - this._graphicViewMarkersGroup.addLayer(this._graphicViewMarkers[viewId]); } + else { + this._graphicViewMarkers[viewId].bounds = L.LatLngBounds.createDefault(); + } + + this._onUpdateGraphicViewSelection(viewId); }, _onCellCursorMsg: function (textMsg) { @@ -1153,7 +1147,7 @@ L.TileLayer = L.GridLayer.extend({ }, // Update colored non-blinking view cursor - _onUpdateViewCursor: function(viewId) { + _onUpdateViewCursor: function (viewId) { if (typeof this._viewCursors[viewId] !== 'object' || typeof this._viewCursors[viewId].bounds !== 'object') { return; @@ -1189,6 +1183,31 @@ L.TileLayer = L.GridLayer.extend({ } }, + _onUpdateGraphicViewSelection: function (viewId) { + var viewBounds = this._graphicViewMarkers[viewId].bounds; + var viewMarker = this._graphicViewMarkers[viewId].marker; + var viewPart = this._graphicViewMarkers[viewId].part; + + if (!this._isEmptyRectangle(viewBounds) && + (this._docType === 'text' || this._selectedPart === viewPart)) { + if (!viewMarker) { + viewMarker = L.rectangle(viewBounds, { + pointerEvents: 'none', + fill: false, + color: L.LOUtil.getViewIdHexColor(viewId) + }); + this._graphicViewMarkers[viewId].marker = viewMarker; + } + else { + viewMarker.setBounds(viewBounds); + } + this._map.addLayer(viewMarker); + } + else if (viewMarker) { + this._map.removeLayer(viewMarker); + } + }, + _updateViewCursors: function () { for (var key in this._viewCursors) { this._onUpdateViewCursor(key); @@ -1201,6 +1220,12 @@ L.TileLayer = L.GridLayer.extend({ } }, + _updateGraphicViewSelections: function () { + for (var key in this._graphicViewMarkers) { + this._onUpdateGraphicViewSelection(key); + } + }, + // Update dragged graphics selection resize. _onGraphicEdit: function (e) { if (!e.handle) { return; } commit 8c9053c457c647ce424522e464758d0c6e13a23b Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Tue Sep 13 08:41:51 2016 +0200 MessageQueue: avoid allocation of unnecessary temporary strings Change-Id: I9162407020d84d0c92c9d1c6dfab806c67f3b873 (cherry picked from commit 74a29c4ee4d4a232e1f2fa8d5c6e6ae835eb05e0) diff --git a/loolwsd/MessageQueue.cpp b/loolwsd/MessageQueue.cpp index b4a9c20..027051e 100644 --- a/loolwsd/MessageQueue.cpp +++ b/loolwsd/MessageQueue.cpp @@ -120,7 +120,7 @@ void TileQueue::put_impl(const Payload& value) Log::error(std::to_string(i) + ": " + oldMsg); if (newMsg == oldMsg) { - Log::trace("Replacing duplicate tile: " + oldMsg + " -> " + newMsg); + Log::trace() << "Replacing duplicate tile: " << oldMsg << " -> " << newMsg << Log::end; _queue[i] = value; return; } commit 82ac9f758bca72d9b65a6ea187eea47bb0fd312d Author: Tor Lillqvist <t...@collabora.com> Date: Mon Sep 12 13:08:29 2016 +0300 Update to match core master Just two small comment and whitespace changes. (cherry picked from commit c56888159a12b8709dbf5d4e987eaac5a70c2eb0) diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h index 6a8388d..53162a3 100644 --- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h +++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h @@ -244,7 +244,6 @@ struct _LibreOfficeKitDocumentClass const int nTileWidth, const int nTileHeight); - /// @see lok::Document::setCallbackLatch(). void (*setCallbackLatch) (LibreOfficeKitDocument* pThis, bool bCallbackLatch); diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h index 324318c..8aef838 100644 --- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h +++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKitEnums.h @@ -284,7 +284,7 @@ typedef enum * { * "classification": "error" | "warning" | "info" * "kind": "network" etc. - * "code": 403 | 404 | ... + * "code": a structured 32-bit error code, the ErrCode from LibreOffice's <tools/errcode.hxx> * "message": freeform description * } */ commit 9b4394bc9164378cd795bc025f9e991b1d8864e5 Author: Miklos Vajna <vmik...@collabora.co.uk> Date: Mon Sep 12 08:15:33 2016 +0200 LOOLKit: use std::move() in Document ctor Change-Id: I25aeb5285465385858180747f441a69b858c4fe6 (cherry picked from commit 6006e2200151843efa983107e4039f9935e5906b) diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 94d02ef..73ade5b 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -405,7 +405,7 @@ public: _jailId(jailId), _docKey(docKey), _url(url), - _queue(queue), + _queue(std::move(queue)), _ws(ws), _docPassword(""), _haveDocPassword(false), commit 4e683ac577c6347913ff75abd3c9ce5b727641f7 Author: Marco Cecchetti <marco.cecche...@collabora.com> Date: Sat Sep 10 21:07:12 2016 +0200 loolwsd: we use callbacks latch for not missing messages sent very early - lok::Document::setCallbackLatch: used on doument loading for set/unset the latch - now cell cursors of other views are correctly notified to the new view (cherry picked from commit ed983f17cbf2627ff7b1b1a04af03449bd18ff6b) diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp index 93a2312..9c3e3dc 100644 --- a/loolwsd/ChildSession.cpp +++ b/loolwsd/ChildSession.cpp @@ -356,6 +356,7 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT // Inform this view of other views _docManager.notifyCurrentViewOfOtherViews(getId()); + _loKitDocument->setCallbackLatch(false); Log::info("Loaded session " + getId()); return true; diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 94c0846..94d02ef 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -1139,6 +1139,7 @@ private: _viewIdToCallbackDescr.emplace(viewId, std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId }))); _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get()); + _loKitDocument->setCallbackLatch(true); Log::info() << "Document [" << _url << "] view [" << viewId << "] loaded, leaving " diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp index 5881c77..d718edd 100644 --- a/loolwsd/LibreOfficeKit.hpp +++ b/loolwsd/LibreOfficeKit.hpp @@ -464,6 +464,17 @@ public: nTileWidth, nTileHeight); } + /** + * Enable/disable callbacks latch. LOK will set the latch when it wants to + * queue new callbacks but not flush them. + * + * @param bCallbackLatch: true enables the latch, false disables it. + */ + inline void setCallbackLatch(bool bCallbackLatch) + { + _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch); + } + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h index 81d65c1..6a8388d 100644 --- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h +++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h @@ -244,6 +244,11 @@ struct _LibreOfficeKitDocumentClass const int nTileWidth, const int nTileHeight); + + /// @see lok::Document::setCallbackLatch(). + void (*setCallbackLatch) (LibreOfficeKitDocument* pThis, + bool bCallbackLatch); + #endif // defined LOK_USE_UNSTABLE_API || defined LIBO_INTERNAL_ONLY }; commit 5569a64aac8cb18e910a45b1f1f353fcac44f620 Author: Marco Cecchetti <marco.cecche...@collabora.com> Date: Sat Sep 10 20:51:59 2016 +0200 Revert "loolwsd: we use callbacks latch for not missing messages sent very early" This reverts commit f2106157f31801d96ac8fdf5e74956455ebbf778. (cherry picked from commit edc1302f16d707c14995f4342353e35284934091) diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp index 93c4cf6..93a2312 100644 --- a/loolwsd/ChildSession.cpp +++ b/loolwsd/ChildSession.cpp @@ -357,8 +357,6 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT // Inform this view of other views _docManager.notifyCurrentViewOfOtherViews(getId()); - _loKitDocument->setCallbackLatch(false); - Log::info("Loaded session " + getId()); return true; } diff --git a/loolwsd/LOKitClient.cpp b/loolwsd/LOKitClient.cpp index 13a180e..b882b79 100644 --- a/loolwsd/LOKitClient.cpp +++ b/loolwsd/LOKitClient.cpp @@ -112,7 +112,7 @@ protected: return Application::EXIT_UNAVAILABLE; } - loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr, /*callback latch*/ false); + loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr); loKitDocument->pClass->initializeForRendering(loKitDocument, nullptr); diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 261821b..94c0846 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -1138,7 +1138,7 @@ private: viewId = _loKitDocument->getView(); _viewIdToCallbackDescr.emplace(viewId, std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId }))); - _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get(), /*callback latch*/ true); + _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get()); Log::info() << "Document [" << _url << "] view [" << viewId << "] loaded, leaving " diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp index 700db4b..5881c77 100644 --- a/loolwsd/LibreOfficeKit.hpp +++ b/loolwsd/LibreOfficeKit.hpp @@ -214,27 +214,15 @@ public: } /** - * Enable/disable callbacks latch. LOK will set the latch when it wants to - * queue new callbacks but not flush them. - * - * @param bCallbackLatch: true enables the latch, false disables it. - */ - inline void setCallbackLatch(bool bCallbackLatch) - { - _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch); - } - - /** * Registers a callback. LOK will invoke this function when it wants to * inform the client about events. * * @param pCallback the callback to invoke * @param pData the user data, will be passed to the callback on invocation - * @param bCallbackLatch the event latch status to be set before the callback is registered */ - inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData, bool bCallbackLatch = false) + inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData) { - _pDoc->pClass->registerCallback(_pDoc, pCallback, pData, bCallbackLatch); + _pDoc->pClass->registerCallback(_pDoc, pCallback, pData); } /** diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h index e03163f..81d65c1 100644 --- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h +++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h @@ -147,15 +147,10 @@ struct _LibreOfficeKitDocumentClass void (*initializeForRendering) (LibreOfficeKitDocument* pThis, const char* pArguments); - /// @see lok::Document::setCallbackLatch(). - void (*setCallbackLatch) (LibreOfficeKitDocument* pThis, - bool bCallbackLatch); - /// @see lok::Document::registerCallback(). void (*registerCallback) (LibreOfficeKitDocument* pThis, LibreOfficeKitCallback pCallback, - void* pData, - bool bCallbackLatch); + void* pData); /// @see lok::Document::postKeyEvent void (*postKeyEvent) (LibreOfficeKitDocument* pThis, commit 0a815d0a4b5607b5082aa89b62b5ad7f0cd1149e Author: Marco Cecchetti <marco.cecche...@collabora.com> Date: Fri Sep 9 22:23:51 2016 +0200 loolwsd: we use callbacks latch for not missing messages sent very early - lok::Document::setCallbackLatch: used by a child session for set/unset the latch - lok::Document::registerCallback has a new boolean parameter used for setting the latch state just before the callback is actually registered for a (new) view - now cell cursors of other views are correctly notified to the new view (cherry picked from commit f2106157f31801d96ac8fdf5e74956455ebbf778) diff --git a/loolwsd/ChildSession.cpp b/loolwsd/ChildSession.cpp index 93a2312..93c4cf6 100644 --- a/loolwsd/ChildSession.cpp +++ b/loolwsd/ChildSession.cpp @@ -357,6 +357,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, StringT // Inform this view of other views _docManager.notifyCurrentViewOfOtherViews(getId()); + _loKitDocument->setCallbackLatch(false); + Log::info("Loaded session " + getId()); return true; } diff --git a/loolwsd/LOKitClient.cpp b/loolwsd/LOKitClient.cpp index b882b79..13a180e 100644 --- a/loolwsd/LOKitClient.cpp +++ b/loolwsd/LOKitClient.cpp @@ -112,7 +112,7 @@ protected: return Application::EXIT_UNAVAILABLE; } - loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr); + loKitDocument->pClass->registerCallback(loKitDocument, myCallback, nullptr, /*callback latch*/ false); loKitDocument->pClass->initializeForRendering(loKitDocument, nullptr); diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 94c0846..261821b 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -1138,7 +1138,7 @@ private: viewId = _loKitDocument->getView(); _viewIdToCallbackDescr.emplace(viewId, std::unique_ptr<CallbackDescriptor>(new CallbackDescriptor({ this, viewId }))); - _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get()); + _loKitDocument->registerCallback(ViewCallback, _viewIdToCallbackDescr[viewId].get(), /*callback latch*/ true); Log::info() << "Document [" << _url << "] view [" << viewId << "] loaded, leaving " diff --git a/loolwsd/LibreOfficeKit.hpp b/loolwsd/LibreOfficeKit.hpp index 5881c77..700db4b 100644 --- a/loolwsd/LibreOfficeKit.hpp +++ b/loolwsd/LibreOfficeKit.hpp @@ -214,15 +214,27 @@ public: } /** + * Enable/disable callbacks latch. LOK will set the latch when it wants to + * queue new callbacks but not flush them. + * + * @param bCallbackLatch: true enables the latch, false disables it. + */ + inline void setCallbackLatch(bool bCallbackLatch) + { + _pDoc->pClass->setCallbackLatch(_pDoc, bCallbackLatch); + } + + /** * Registers a callback. LOK will invoke this function when it wants to * inform the client about events. * * @param pCallback the callback to invoke * @param pData the user data, will be passed to the callback on invocation + * @param bCallbackLatch the event latch status to be set before the callback is registered */ - inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData) + inline void registerCallback(LibreOfficeKitCallback pCallback, void* pData, bool bCallbackLatch = false) { - _pDoc->pClass->registerCallback(_pDoc, pCallback, pData); + _pDoc->pClass->registerCallback(_pDoc, pCallback, pData, bCallbackLatch); } /** diff --git a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h index 81d65c1..e03163f 100644 --- a/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h +++ b/loolwsd/bundled/include/LibreOfficeKit/LibreOfficeKit.h @@ -147,10 +147,15 @@ struct _LibreOfficeKitDocumentClass void (*initializeForRendering) (LibreOfficeKitDocument* pThis, const char* pArguments); + /// @see lok::Document::setCallbackLatch(). + void (*setCallbackLatch) (LibreOfficeKitDocument* pThis, + bool bCallbackLatch); + /// @see lok::Document::registerCallback(). void (*registerCallback) (LibreOfficeKitDocument* pThis, LibreOfficeKitCallback pCallback, - void* pData); + void* pData, + bool bCallbackLatch); /// @see lok::Document::postKeyEvent void (*postKeyEvent) (LibreOfficeKitDocument* pThis, commit 8321ba0369fccd99c43acd4cfbe3ae083c82fc4a Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 8 16:48:56 2016 -0400 loleaflet: avoid undefined variables when map not loaded (cherry picked from commit 46a314206b720858127e254d2ae499e12fa53b60) diff --git a/loleaflet/src/map/Map.js b/loleaflet/src/map/Map.js index 1d01ff7..e2204b4 100644 --- a/loleaflet/src/map/Map.js +++ b/loleaflet/src/map/Map.js @@ -790,6 +790,8 @@ L.Map = L.Evented.extend({ }, _onLostFocus: function () { + if (!this._loaded) { return; } + var doclayer = this._docLayer; if (doclayer._isCursorVisible && doclayer._isCursorOverlayVisible) { doclayer._visibleCursorOnLostFocus = doclayer._visibleCursor; @@ -802,6 +804,8 @@ L.Map = L.Evented.extend({ }, _onGotFocus: function () { + if (!this._loaded) { return; } + var doclayer = this._docLayer; if (doclayer._isCursorVisibleOnLostFocus && doclayer._isCursorOverlayVisibleOnLostFocus) { // we restore the old cursor position by a small delay, so that if the user clicks commit 1ca70704f8f4fbb0bb54ebbfeaf3dd308a58f7a8 Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 8 12:16:51 2016 -0400 loolwsd: test: cell view cursor (cherry picked from commit e9f3fd4e560089aa5402b4b12b48a7483185893a) diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp index 79bb226..a6fe190 100644 --- a/loolwsd/test/httpwstest.cpp +++ b/loolwsd/test/httpwstest.cpp @@ -91,6 +91,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testOptimalResize); CPPUNIT_TEST(testInvalidateViewCursor); CPPUNIT_TEST(testViewCursorVisible); + CPPUNIT_TEST(testCellViewCursor); CPPUNIT_TEST_SUITE_END(); @@ -130,6 +131,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture void testOptimalResize(); void testInvalidateViewCursor(); void testViewCursorVisible(); + void testCellViewCursor(); void loadDoc(const std::string& documentURL); @@ -2223,6 +2225,66 @@ void HTTPWSTest::testViewCursorVisible() } } +void HTTPWSTest::testCellViewCursor() +{ + try + { + int docSheet = -1; + int docSheets = 0; + int docHeight = 0; + int docWidth = 0; + int docViewId = -1; + int itView = 0; + + // 0..N Views + std::vector<std::shared_ptr<Poco::Net::WebSocket>> views; + + // Load a document + std::string documentPath, documentURL, response, text; + getDocumentPathAndURL("empty.ods", documentPath, documentURL); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL); + Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response); + + sendTextFrame(socket1, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1)); + + // Check document size + sendTextFrame(socket1, "status"); + getResponseMessage(socket1, "status:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty()); + parseDocSize(response, "spreadsheet", docSheet, docSheets, docWidth, docHeight, docViewId); + + // Click to show a cell cursor + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/10, docHeight/10); + sendTextFrame(socket1, text); text.clear(); + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/10, docHeight/10); + sendTextFrame(socket1, text); + getResponseMessage(socket1, "cellcursor:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a cellcursor: message as expected", !response.empty()); + + // Connect 0..N Views + for (itView = 0; itView < 10; ++itView) + { + views.emplace_back(connectLOKit(_uri, request, _response)); + } + + // Load 0..N view and expect to receive cellviewcursor + for (auto socketView : views) + { + sendTextFrame(*socketView, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(*socketView, "", true)); + + // Expected to receive cellviewcursor second view + getResponseMessage(*socketView, "cellviewcursor:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a cellviewcursor: message as expected", !response.empty()); + } + } + catch (const Poco::Exception& exc) + { + CPPUNIT_FAIL(exc.displayText()); + } +} CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest); commit c5a59d279da33ba7dfc689fed7b82f6fa6bf8052 Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 8 12:11:07 2016 -0400 loleaflet: fix part not taken into account for cell cursors (cherry picked from commit 878fff5ebb78b58bbcb5c49516eed8f7215111e5) diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js index db63f95..45fd68d 100644 --- a/loleaflet/src/control/Parts.js +++ b/loleaflet/src/control/Parts.js @@ -32,6 +32,7 @@ L.Map.include({ }); this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart); docLayer._updateViewCursors(); + docLayer._updateCellViewCursors(); docLayer._clearSelections(); docLayer._updateOnChangePart(); docLayer._pruneTiles(); diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 6fb3c1b..8b40da6 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -653,6 +653,7 @@ L.TileLayer = L.GridLayer.extend({ this._twipsToLatLng(bottomRightTwips, this._map.getZoom())); } + this._cellViewCursors[viewId].part = parseInt(obj.part); this._onUpdateCellViewCursor(viewId); }, @@ -661,23 +662,19 @@ L.TileLayer = L.GridLayer.extend({ return; var cellViewCursorsMarker = this._cellViewCursors[viewId].marker; - if (!this._isEmptyRectangle(this._cellViewCursors[viewId].bounds)) { + var viewPart = this._cellViewCursors[viewId].part; - if (cellViewCursorsMarker) { - this._map.removeLayer(cellViewCursorsMarker); - } - cellViewCursorsMarker = L.rectangle(this._cellViewCursors[viewId].bounds, {fill: false, color: L.LOUtil.getViewIdHexColor(viewId), weight: 2}); + if (!this._isEmptyRectangle(this._cellViewCursors[viewId].bounds) && this._selectedPart === viewPart) { if (!cellViewCursorsMarker) { - this._map.fire('error', {msg: 'Cell View Cursor marker initialization', cmd: 'cellViewCursor', kind: 'failed', id: 1}); - return; + cellViewCursorsMarker = L.rectangle(this._cellViewCursors[viewId].bounds, {fill: false, color: L.LOUtil.getViewIdHexColor(viewId), weight: 2}); + this._cellViewCursors[viewId].marker = cellViewCursorsMarker; } + cellViewCursorsMarker.setBounds(this._cellViewCursors[viewId].bounds); this._map.addLayer(cellViewCursorsMarker); } else if (cellViewCursorsMarker) { this._map.removeLayer(cellViewCursorsMarker); } - - this._cellViewCursors[viewId].marker = cellViewCursorsMarker; }, _onViewCursorVisibleMsg: function(textMsg) { @@ -1198,6 +1195,12 @@ L.TileLayer = L.GridLayer.extend({ } }, + _updateCellViewCursors: function () { + for (var key in this._cellViewCursors) { + this._onUpdateCellViewCursor(key); + } + }, + // Update dragged graphics selection resize. _onGraphicEdit: function (e) { if (!e.handle) { return; } commit d1af32048d9d3875c14030bb63fddc42320948cd Author: Henry Castro <hcas...@collabora.com> Date: Thu Sep 8 10:26:25 2016 -0400 loolwsd: test: view cursor visible (cherry picked from commit f751d36c8f3f84948acd77e5c14b18597578f0fe) diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp index 4af1fea..79bb226 100644 --- a/loolwsd/test/httpwstest.cpp +++ b/loolwsd/test/httpwstest.cpp @@ -90,6 +90,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testColumnRowResize); CPPUNIT_TEST(testOptimalResize); CPPUNIT_TEST(testInvalidateViewCursor); + CPPUNIT_TEST(testViewCursorVisible); CPPUNIT_TEST_SUITE_END(); @@ -128,6 +129,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture void testColumnRowResize(); void testOptimalResize(); void testInvalidateViewCursor(); + void testViewCursorVisible(); void loadDoc(const std::string& documentURL); @@ -2172,6 +2174,56 @@ void HTTPWSTest::testInvalidateViewCursor() } } +void HTTPWSTest::testViewCursorVisible() +{ + try + { + int docSlide = -1; + int docSlides = 0; + int docHeight = 0; + int docWidth = 0; + int docViewId = -1; + + // Load a document + std::string documentPath, documentURL, response, text; + getDocumentPathAndURL("viewcursor.odp", documentPath, documentURL); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL); + Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response); + + sendTextFrame(socket1, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1)); + + // Check document size + sendTextFrame(socket1, "status"); + getResponseMessage(socket1, "status:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty()); + parseDocSize(response, "presentation", docSlide, docSlides, docWidth, docHeight, docViewId); + + // Click to show a cursor + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/2, docHeight/6); + sendTextFrame(socket1, text); text.clear(); + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/2, docHeight/6); + sendTextFrame(socket1, text); + getResponseMessage(socket1, "cursorvisible:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a cursorvisible: message as expected", !response.empty()); + + // Connect and load second view. + Poco::Net::WebSocket socket2 = *connectLOKit(_uri, request, _response); + sendTextFrame(socket2, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket2, "", true)); + + // Expected to receive viewcursorvisible second view + getResponseMessage(socket2, "viewcursorvisible:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a viewcursorvisible: message as expected", !response.empty()); + } + catch (const Poco::Exception& exc) + { + CPPUNIT_FAIL(exc.displayText()); + } +} + + CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit a84d16ece323de3423ebf69ec0b0424c8dc185b9 Author: Pranav Kant <pran...@collabora.co.uk> Date: Wed Sep 7 21:46:41 2016 +0200 loleaflet: Implement object/image multiple view selections Change-Id: Ifc2d6f95fa48a947fc043bf48d582ab549fa6172 (cherry picked from commit d2bf039d59d0e4c190535453b2ed8b8faf9b9abd) diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 9f70ac2..6fb3c1b 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -102,6 +102,9 @@ L.TileLayer = L.GridLayer.extend({ // View cell cursors with viewId to 'cursor info' mapping. this._cellViewCursors = {}; + // Graphic view selection rectangles + this._graphicViewMarkers = {}; + this._lastValidPart = -1; // Cursor marker this._cursorMarker = null; @@ -143,6 +146,10 @@ L.TileLayer = L.GridLayer.extend({ map.addLayer(this._viewSelectionsGroup); this._viewSelections = {}; + this._graphicViewMarkersGroup = new L.LayerGroup(); + map.addLayer(this._graphicViewMarkersGroup); + this._graphicViewMarkers = {}; + this._searchResultsLayer = new L.LayerGroup(); map.addLayer(this._searchResultsLayer); @@ -378,6 +385,9 @@ L.TileLayer = L.GridLayer.extend({ else if (textMsg.startsWith('textviewselection:')) { this._onTextViewSelectionMsg(textMsg); } + else if (textMsg.startsWith('graphicviewselection:')) { + this._onGraphicViewSelectionMsg(textMsg); + } }, _onCommandValuesMsg: function (textMsg) { @@ -479,6 +489,36 @@ L.TileLayer = L.GridLayer.extend({ this._onUpdateGraphicSelection(); }, + _onGraphicViewSelectionMsg: function (textMsg) { + textMsg = textMsg.substring('graphicviewselection:'.length + 1); + var obj = JSON.parse(textMsg); + var viewId = parseInt(obj.viewId); + + // Ignore yourself + if (viewId === this._viewId) { + return; + } + + var strTwips = obj.selection.match(/\d+/g); + if (this._graphicViewMarkers[viewId]) { + this._graphicViewMarkersGroup.removeLayer(this._graphicViewMarkers[viewId]); + } + if (strTwips != null) { + var topLeftTwips = new L.Point(parseInt(strTwips[0]), parseInt(strTwips[1])); + var offset = new L.Point(parseInt(strTwips[2]), parseInt(strTwips[3])); + var bottomRightTwips = topLeftTwips.add(offset); + var graphicSelection = new L.LatLngBounds( + this._twipsToLatLng(topLeftTwips, this._map.getZoom()), + this._twipsToLatLng(bottomRightTwips, this._map.getZoom())); + this._graphicViewMarkers[viewId] = L.rectangle(graphicSelection, { + pointerEvents: 'none', + fill: false, + color: L.LOUtil.getViewIdHexColor(viewId) + }); + this._graphicViewMarkersGroup.addLayer(this._graphicViewMarkers[viewId]); + } + }, + _onCellCursorMsg: function (textMsg) { if (!this._cellCursor) { this._cellCursor = L.LatLngBounds.createDefault(); commit fee1819b86b0f8ec7aee6e69506f37bd17c6a422 Author: Pranav Kant <pran...@collabora.co.uk> Date: Wed Sep 7 16:52:18 2016 +0200 Fix comment Change-Id: Ib43bcc7378590e71b98856c11218fd99ad90d32d (cherry picked from commit 33a5c4bc7083154aa8fa93fd663d1e38bc8269e5) diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index e3519fb..94c0846 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -936,7 +936,7 @@ private: } } - /// Notify all currently active sessions of session with given 'sessionId' + /// Notify all currently active sessions about session with given 'sessionId' void notifyOtherSessions(const std::string& sessionId, const std::string& message) const override { std::unique_lock<std::mutex> lock(_mutex); @@ -954,7 +954,7 @@ private: } } - /// Notify session (with given 'sessionId') of other currently active views + /// Notify session (with given 'sessionId'), if active, of other existing views void notifyCurrentViewOfOtherViews(const std::string& sessionId) const override { std::unique_lock<std::mutex> lock(_mutex); commit 5a5f93dc10d34e03f83653a042bc44de805e3d23 Author: Henry Castro <hcas...@collabora.com> Date: Wed Sep 7 12:21:34 2016 -0400 loolwsd: test: invalidate view cursor (cherry picked from commit d4d764cee580c30ab5cc776118ea6008ca40c7f2) diff --git a/loolwsd/test/data/viewcursor.odp b/loolwsd/test/data/viewcursor.odp new file mode 100755 index 0000000..a6a3561 Binary files /dev/null and b/loolwsd/test/data/viewcursor.odp differ diff --git a/loolwsd/test/helpers.hpp b/loolwsd/test/helpers.hpp index 0a7d003..2401243 100644 --- a/loolwsd/test/helpers.hpp +++ b/loolwsd/test/helpers.hpp @@ -448,7 +448,6 @@ void parseDocSize(const std::string& message, const std::string& type, int& part, int& parts, int& width, int& height, int& viewid) { Poco::StringTokenizer tokens(message, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM); - CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(6), tokens.count()); // Expected format is something like 'type= parts= current= width= height='. const std::string text = tokens[0].substr(std::string("type=").size()); diff --git a/loolwsd/test/httpwstest.cpp b/loolwsd/test/httpwstest.cpp index f28c5d1..4af1fea 100644 --- a/loolwsd/test/httpwstest.cpp +++ b/loolwsd/test/httpwstest.cpp @@ -89,6 +89,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture CPPUNIT_TEST(testStateUnoCommand); CPPUNIT_TEST(testColumnRowResize); CPPUNIT_TEST(testOptimalResize); + CPPUNIT_TEST(testInvalidateViewCursor); CPPUNIT_TEST_SUITE_END(); @@ -126,6 +127,7 @@ class HTTPWSTest : public CPPUNIT_NS::TestFixture void testStateUnoCommand(); void testColumnRowResize(); void testOptimalResize(); + void testInvalidateViewCursor(); void loadDoc(const std::string& documentURL); @@ -2121,6 +2123,55 @@ void HTTPWSTest::testOptimalResize() } } +void HTTPWSTest::testInvalidateViewCursor() +{ + try + { + int docSlide = -1; + int docSlides = 0; + int docHeight = 0; + int docWidth = 0; + int docViewId = -1; + + // Load a document + std::string documentPath, documentURL, response, text; + getDocumentPathAndURL("viewcursor.odp", documentPath, documentURL); + + Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, documentURL); + Poco::Net::WebSocket socket1 = *connectLOKit(_uri, request, _response); + + sendTextFrame(socket1, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket1)); + + // Check document size + sendTextFrame(socket1, "status"); + getResponseMessage(socket1, "status:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a status: message as expected", !response.empty()); + parseDocSize(response, "presentation", docSlide, docSlides, docWidth, docHeight, docViewId); + + // Click to show a cursor + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttondown"), docWidth/2, docHeight/6); + sendTextFrame(socket1, text); text.clear(); + Poco::format(text, "mouse type=%s x=%d y=%d count=1 buttons=1 modifier=0", std::string("buttonup"), docWidth/2, docHeight/6); + sendTextFrame(socket1, text); + getResponseMessage(socket1, "invalidatecursor:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a invalidatecursor: message as expected", !response.empty()); + + // Connect and load second view. + Poco::Net::WebSocket socket2 = *connectLOKit(_uri, request, _response); + sendTextFrame(socket2, "load url=" + documentURL); + CPPUNIT_ASSERT_MESSAGE("cannot load the document " + documentURL, isDocumentLoaded(socket2, "", true)); + + // Expected to receive invalidateviewcursor second view + getResponseMessage(socket2, "invalidateviewcursor:", response, false); + CPPUNIT_ASSERT_MESSAGE("did not receive a invalidateviewcursor: message as expected", !response.empty()); + } + catch (const Poco::Exception& exc) + { + CPPUNIT_FAIL(exc.displayText()); + } +} + CPPUNIT_TEST_SUITE_REGISTRATION(HTTPWSTest); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ commit 8d079bc009d1b7f0aaaadf41977859da6be7706a Author: Henry Castro <hcas...@collabora.com> Date: Wed Sep 7 12:16:23 2016 -0400 loleaflet: fix part not taken into account for cursors Also refactor cursor marker (cherry picked from commit a666c01c03ac3bd680239e22b84ba4fa2f6971e6) diff --git a/loleaflet/src/control/Parts.js b/loleaflet/src/control/Parts.js index cf90a14..db63f95 100644 --- a/loleaflet/src/control/Parts.js +++ b/loleaflet/src/control/Parts.js @@ -31,6 +31,7 @@ L.Map.include({ docType: docLayer._docType }); this._socket.sendMessage('setclientpart part=' + docLayer._selectedPart); + docLayer._updateViewCursors(); docLayer._clearSelections(); docLayer._updateOnChangePart(); docLayer._pruneTiles(); diff --git a/loleaflet/src/layer/marker/Cursor.js b/loleaflet/src/layer/marker/Cursor.js index cebc657..7fade22 100644 --- a/loleaflet/src/layer/marker/Cursor.js +++ b/loleaflet/src/layer/marker/Cursor.js @@ -8,18 +8,25 @@ L.Cursor = L.Layer.extend({ opacity: 1 }, - initialize: function (latlng, options) { + initialize: function (latlng, size, options) { L.setOptions(this, options); this._latlng = L.latLng(latlng); + this._size = L.point(size); }, onAdd: function () { - this._initLayout(); + if (!this._container) { + this._initLayout(); + } + this.update(); + this.getPane().appendChild(this._container); }, onRemove: function () { - L.DomUtil.remove(this._container); + if (this._container) { + this.getPane().removeChild(this._container); + } }, getEvents: function () { @@ -32,16 +39,18 @@ L.Cursor = L.Layer.extend({ return this._latlng; }, - setLatLng: function (latlng) { + setLatLng: function (latlng, size) { var oldLatLng = this._latlng; this._latlng = L.latLng(latlng); + this._size = L.point(size); this.update(); return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng}); }, update: function () { - if (this._container) { + if (this._container && this._map) { var pos = this._map.latLngToLayerPoint(this._latlng).round(); + this._setSize(); this._setPos(pos); } return this; @@ -71,10 +80,6 @@ L.Cursor = L.Layer.extend({ L.DomEvent .disableClickPropagation(this._cursor) .disableScrollPropagation(this._container); - - if (this._container) { - this.getPane().appendChild(this._container); - } }, _setPos: function (pos) { @@ -97,14 +102,14 @@ L.Cursor = L.Layer.extend({ L.DomUtil.setOpacity(this._container, opacity); }, - setSize: function (size) { - this._cursor.style.height = size.y + 'px'; - this._container.style.top = '-' + (this._container.clientHeight - size.y - 2) / 2 + 'px'; + _setSize: function () { + this._cursor.style.height = this._size.y + 'px'; + this._container.style.top = '-' + (this._container.clientHeight - this._size.y - 2) / 2 + 'px'; } }); -L.cursor = function (latlng, options) { - return new L.Cursor(latlng, options); +L.cursor = function (latlng, size, options) { + return new L.Cursor(latlng, size, options); }; L.Cursor.getCursorURL = function (localPath) { diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index 388582f..9f70ac2 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -576,8 +576,12 @@ L.TileLayer = L.GridLayer.extend({ this._viewCursors[viewId].bounds = new L.LatLngBounds( this._twipsToLatLng(topLeftTwips, this._map.getZoom()), this._twipsToLatLng(bottomRightTwips, this._map.getZoom())), - this._viewCursors[viewId].part = obj.part; - this._viewCursors[viewId].visible = true; + this._viewCursors[viewId].part = parseInt(obj.part); + + // FIXME. Server not sending view visible cursor + if (typeof this._viewCursors[viewId].visible === 'undefined') { + this._viewCursors[viewId].visible = true; + } this._onUpdateViewCursor(viewId); }, @@ -1098,14 +1102,12 @@ L.TileLayer = L.GridLayer.extend({ if (this._map._permission === 'edit' && this._isCursorVisible && this._isCursorOverlayVisible && !this._isEmptyRectangle(this._visibleCursor)) { - if (this._cursorMarker) { - this._map.removeLayer(this._cursorMarker); + if (!this._cursorMarker) { + this._cursorMarker = L.cursor(null, null, {blink: true}); } - this._cursorMarker = L.cursor(cursorPos, {blink: true}); + this._cursorMarker.setLatLng(cursorPos, pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom()))); this._map.addLayer(this._cursorMarker); - this._cursorMarker.setSize(pixBounds.getSize().multiplyBy( - this._map.getZoomScale(this._map.getZoom()))); } else if (this._cursorMarker) { this._map.removeLayer(this._cursorMarker); @@ -1125,27 +1127,35 @@ L.TileLayer = L.GridLayer.extend({ var viewCursorPos = this._viewCursors[viewId].bounds.getNorthWest(); var viewCursorMarker = this._viewCursors[viewId].marker; var viewCursorVisible = this._viewCursors[viewId].visible; - - if (viewCursorVisible && !this._isEmptyRectangle(this._viewCursors[viewId].bounds)) { - if (viewCursorMarker) { - this._map.removeLayer(viewCursorMarker); + var viewPart = this._viewCursors[viewId].part; + + if (viewCursorVisible && !this._isEmptyRectangle(this._viewCursors[viewId].bounds) && + (this._docType === 'text' || this._selectedPart === viewPart)) { + if (!viewCursorMarker) { + var viewCursorOptions = { + color: L.LOUtil.getViewIdHexColor(viewId), + blink: false, + header: true, // we want a 'hat' to our view cursors (which will contain view user names) + headerTimeout: 3000, // hide after some interval + headerName: this._map.getViewName(viewId) + }; + + viewCursorMarker = L.cursor(null, null, viewCursorOptions); + this._viewCursors[viewId].marker = viewCursorMarker; } - var viewCursorOptions = { - color: L.LOUtil.getViewIdHexColor(viewId), - blink: false, - header: true, // we want a 'hat' to our view cursors (which will contain view user names) - headerTimeout: 3000, // hide after some interval - headerName: this._map.getViewName(viewId) - }; - viewCursorMarker = L.cursor(viewCursorPos, viewCursorOptions); - this._map.addLayer(viewCursorMarker); - viewCursorMarker.setSize(pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom()))); - } else if (viewCursorMarker) { - this._map.removeLayer(this._viewCursors[viewId].marker); + viewCursorMarker.setLatLng(viewCursorPos, pixBounds.getSize().multiplyBy(this._map.getZoomScale(this._map.getZoom()))); + this._map.addLayer(viewCursorMarker); + } + else if (viewCursorMarker) { + this._map.removeLayer(viewCursorMarker); } + }, - this._viewCursors[viewId].marker = viewCursorMarker; + _updateViewCursors: function () { + for (var key in this._viewCursors) { + this._onUpdateViewCursor(key); + } }, // Update dragged graphics selection resize. commit e3d55444f01a26fa95144059cbca7b076c903086 Author: Pranav Kant <pran...@collabora.co.uk> Date: Wed Sep 7 11:37:12 2016 +0200 loolwsd: Comments Change-Id: Ia4e91f18d695ac2249fa1139afbcb5b3f65f87ba (cherry picked from commit 1b0088ab697881f2f54a23366febfe6e18c6bb25) diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index 3d52244..e3519fb 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -936,6 +936,7 @@ private: } } + /// Notify all currently active sessions of session with given 'sessionId' void notifyOtherSessions(const std::string& sessionId, const std::string& message) const override { std::unique_lock<std::mutex> lock(_mutex); @@ -953,6 +954,7 @@ private: } } + /// Notify session (with given 'sessionId') of other currently active views void notifyCurrentViewOfOtherViews(const std::string& sessionId) const override { std::unique_lock<std::mutex> lock(_mutex); commit 559bae76f11d8b12db6e2c8128aaf1646f9e638a Author: Pranav Kant <pran...@collabora.co.uk> Date: Sat Sep 3 02:27:37 2016 +0530 loolwsd: Code reuse, and don't send 'remview' message to inactive clients Use the existing function notifyOtherSessions() to broadcast the message. The function also takes care of sending this message only to active clients. Change-Id: I98d40429de17463167a67cb10ba59381a0cbc525 (cherry picked from commit 3f421be4dab86fcfc2f48c7192cb9a2b79a74c32) diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index bd9928f..3d52244 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -419,7 +419,6 @@ public: Log::info("Document ctor for url [" + _url + "] on child [" + _jailId + "] LOK_VIEW_CALLBACK=" + std::to_string(_multiView) + "."); assert(_loKit && _loKit->get()); - _callbackThread.start(*this); } @@ -910,22 +909,12 @@ private: Log::info("Unloading [" + sessionId + "]."); // Broadcast the demise and removal of session. - { - std::unique_lock<std::mutex> lock(_mutex); - - // We should be removed by this point, otherwise - // our closed connection will throw, if not segfault. - for (const auto& pair : _connections) - { - assert(sessionId != pair.second->getSessionId() && "Unloading connection still lingering."); - pair.second->getSession()->sendTextFrame("remview: " + std::to_string(session.getViewId())); - } + notifyOtherSessions(sessionId, "remview: " + std::to_string(session.getViewId())); - if (_loKitDocument == nullptr) - { - Log::error("Unloading session [" + sessionId + "] without loKitDocument."); - return; - } + if (_loKitDocument == nullptr) + { + Log::error("Unloading session [" + sessionId + "] without loKitDocument."); + return; } --_clientViews; commit b1a0ef733b914cb5cd69f297812216232badfb07 Author: Pranav Kant <pran...@collabora.co.uk> Date: Sat Sep 3 01:51:04 2016 +0530 loolwsd: Don't send addview notification to inactive clients Change-Id: I4771e4bf12057fdc7099e9433d3b7dc2198cb265 (cherry picked from commit 9edb1c2994b9ab5f6d6d75e1e715deb43a0ae3c8) diff --git a/loolwsd/LOOLKit.cpp b/loolwsd/LOOLKit.cpp index b62754d..bd9928f 100644 --- a/loolwsd/LOOLKit.cpp +++ b/loolwsd/LOOLKit.cpp @@ -956,7 +956,7 @@ private: if (it.second->isRunning() && it.second->getSessionId() != sessionId) { auto session = it.second->getSession(); - if (session) + if (session && session->isActive()) { session->sendTextFrame(message); } @@ -976,6 +976,11 @@ private: } auto currentSession = it->second->getSession(); + if (!currentSession->isActive()) + { + return; + } + for (auto& connectionIt: _connections) { if (connectionIt.second->isRunning() && connectionIt.second->getSessionId() != sessionId) commit bf77497f4376837aee91f9e83734a2efe3d5c09a Author: Tor Lillqvist <t...@collabora.com> Date: Fri Sep 2 15:15:40 2016 +0300 Revert "Mention --without-system-nss" That is the default anyway. This reverts commit 894b0a11b0ab6c6e0e428451cb03c0c9997f6ea6. (cherry picked from commit c4733fd4d2dccad9db630339fb906c96e8201d90) diff --git a/loolwsd/README b/loolwsd/README index eb5cb80..fdd648a 100644 --- a/loolwsd/README +++ b/loolwsd/README @@ -22,10 +22,6 @@ On Debian 8 (Linux x86_64) you can use in /etc/apt/sources.list: deb https://www.collaboraoffice.com/apt-poco/ / -On Fedora 24 at least, you must run against a LibreOffice built with ---without-system-nss. Otherwise opening of password-protected -documents will fail. - Building -------- commit ecddb6d479aabdb3d1e5cdf96e29cc0fdb2be8e6 Author: Tor Lillqvist <t...@collabora.com> Date: Fri Sep 2 15:08:46 2016 +0300 Mention --without-system-nss (cherry picked from commit 894b0a11b0ab6c6e0e428451cb03c0c9997f6ea6) diff --git a/loolwsd/README b/loolwsd/README index fdd648a..eb5cb80 100644 --- a/loolwsd/README +++ b/loolwsd/README @@ -22,6 +22,10 @@ On Debian 8 (Linux x86_64) you can use in /etc/apt/sources.list: deb https://www.collaboraoffice.com/apt-poco/ / +On Fedora 24 at least, you must run against a LibreOffice built with +--without-system-nss. Otherwise opening of password-protected +documents will fail. + Building -------- commit 42521e013e8c95a0f457ff3427e86a1e358da2d2 Author: Ashod Nakashian <ashod.nakash...@collabora.co.uk> Date: Fri Sep 2 07:29:59 2016 -0400 loolwsd: don't combine tiles by row to allow for better culling Change-Id: I30c0a9caad51bd77360a97c9f5899742385ccc27 Reviewed-on: https://gerrit.libreoffice.org/28615 Reviewed-by: Ashod Nakashian <ashnak...@gmail.com> Tested-by: Ashod Nakashian <ashnak...@gmail.com> (cherry picked from commit 99d0ee2ac111e7199626f6c17fb7ce723dac9126) diff --git a/loolwsd/DocumentBroker.cpp b/loolwsd/DocumentBroker.cpp index 043218f..fe73d63 100644 --- a/loolwsd/DocumentBroker.cpp +++ b/loolwsd/DocumentBroker.cpp @@ -532,8 +532,7 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined, Log::trace() << "TileCombined request for " << tileCombined.serialize() << Log::end; // Satisfy as many tiles from the cache. - // The rest, group by rows. - std::map<int, std::vector<TileDesc>> rows; + std::vector<TileDesc> tiles; for (auto& tile : tileCombined.getTiles()) { std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile); @@ -587,34 +586,16 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined, } } - const auto tilePosY = tile.getTilePosY(); - auto it = rows.lower_bound(tilePosY); - if (it != rows.end()) - { - it->second.emplace_back(tile); - } - else - { - rows.emplace_hint(it, tilePosY, std::vector<TileDesc>({ tile })); - } + tiles.push_back(tile); } - if (rows.empty()) + for (auto& tile : tiles) { - // Done. - return; - } - - auto& tiles = tileCombined.getTiles(); - for (auto& row : rows) - { - tiles = row.second; - const auto tileMsg = tileCombined.serialize(); + const auto tileMsg = tile.serialize("tile "); Log::debug() << "TileCombined residual request for " << tileMsg << Log::end; // Forward to child to render. - const std::string request = "tilecombine " + tileMsg; - _childProcess->getWebSocket()->sendFrame(request.data(), request.size()); + _childProcess->getWebSocket()->sendFrame(tileMsg.data(), tileMsg.size()); } } _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits