[Libreoffice-commits] online.git: kit/ChildSession.cpp kit/ChildSession.hpp kit/Kit.cpp loleaflet/dist loleaflet/src test/WhiteBoxTests.cpp
Aditya Dewan
iit2015097 at iiita.ac.in
Thu Jul 6 07:47:07 UTC 2017
kit/ChildSession.cpp | 28 +++++++++++++
kit/ChildSession.hpp | 9 ++++
kit/Kit.cpp | 72 +++++++++++++++++++++++++++++++---
loleaflet/dist/editor.css | 18 ++++++++
loleaflet/dist/toolbar/toolbar.js | 71 +++++++++++++++++++++++++++++++--
loleaflet/src/layer/marker/Cursor.js | 6 +-
loleaflet/src/layer/tile/TileLayer.js | 19 ++++++++
test/WhiteBoxTests.cpp | 9 ++++
8 files changed, 220 insertions(+), 12 deletions(-)
New commits:
commit 3a43f691167c6ad08ffc5670384ca3ab8cadcbe2
Author: Aditya Dewan <iit2015097 at iiita.ac.in>
Date: Tue Jun 27 17:14:50 2017 +0530
tdf#108341 automatic editor switch and follow added
Change-Id: I5129256a0633916a3ca2cb05ccba39a5f4a5d398
Reviewed-on: https://gerrit.libreoffice.org/39299
Reviewed-by: pranavk <pranavk at collabora.co.uk>
Tested-by: pranavk <pranavk at collabora.co.uk>
diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp
index fcc750b7..0a4a59e4 100644
--- a/kit/ChildSession.cpp
+++ b/kit/ChildSession.cpp
@@ -367,6 +367,8 @@ bool ChildSession::loadDocument(const char * /*buffer*/, int /*length*/, const s
// Inform everyone (including this one) about updated view info
_docManager.notifyViewInfo();
+ sendTextFrame("editor: " + std::to_string(_docManager.getEditorId()));
+
LOG_INF("Loaded session " << getId());
return true;
@@ -1027,6 +1029,31 @@ void ChildSession::rememberEventsForInactiveUser(const int type, const std::stri
}
}
+void ChildSession::updateSpeed() {
+
+ std::chrono::steady_clock::time_point now(std::chrono::steady_clock::now());
+
+ while(_cursorInvalidatedEvent.size() != 0 &&
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - _cursorInvalidatedEvent.front()).count() > _eventStorageIntervalMs)
+ {
+ _cursorInvalidatedEvent.pop();
+ }
+ _cursorInvalidatedEvent.push(now);
+ _docManager.updateEditorSpeeds(_viewId, _cursorInvalidatedEvent.size());
+}
+
+int ChildSession::getSpeed() {
+
+ std::chrono::steady_clock::time_point now(std::chrono::steady_clock::now());
+
+ while(_cursorInvalidatedEvent.size() > 0 &&
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - _cursorInvalidatedEvent.front()).count() > _eventStorageIntervalMs)
+ {
+ _cursorInvalidatedEvent.pop();
+ }
+ return _cursorInvalidatedEvent.size();
+}
+
void ChildSession::loKitCallback(const int type, const std::string& payload)
{
const auto typeName = LOKitHelper::kitCallbackTypeToString(type);
@@ -1101,6 +1128,7 @@ void ChildSession::loKitCallback(const int type, const std::string& payload)
}
break;
case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
+ updateSpeed();
sendTextFrame("invalidatecursor: " + payload);
break;
case LOK_CALLBACK_TEXT_SELECTION:
diff --git a/kit/ChildSession.hpp b/kit/ChildSession.hpp
index 7faef0ed..446ffca4 100644
--- a/kit/ChildSession.hpp
+++ b/kit/ChildSession.hpp
@@ -12,6 +12,7 @@
#include <mutex>
#include <unordered_map>
+#include <queue>
#define LOK_USE_UNSTABLE_API
#include <LibreOfficeKit/LibreOfficeKit.hxx>
@@ -47,6 +48,9 @@ public:
/// Send updated view info to all active sessions.
virtual void notifyViewInfo() = 0;
+ virtual void updateEditorSpeeds(int id, int speed) = 0;
+
+ virtual int getEditorId() = 0;
/// Get a view ID <-> UserInfo map.
virtual std::map<int, UserInfo> getViewInfo() = 0;
@@ -139,6 +143,8 @@ public:
const std::string& getViewUserId() const { return _userId; }
const std::string& getViewUserName() const { return _userName; }
const std::string& getViewUserExtraInfo() const { return _userExtraInfo; }
+ void updateSpeed();
+ int getSpeed();
void loKitCallback(const int type, const std::string& payload);
@@ -190,6 +196,9 @@ private:
const std::string _jailId;
IDocumentManager& _docManager;
+ std::queue<std::chrono::steady_clock::time_point> _cursorInvalidatedEvent;
+ const unsigned _eventStorageIntervalMs = 15*1000;
+
/// View ID, returned by createView() or 0 by default.
int _viewId;
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index 8036503b..d3d865d0 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -516,7 +516,9 @@ public:
_isDocPasswordProtected(false),
_docPasswordType(PasswordType::ToView),
_stop(false),
- _isLoading(0)
+ _isLoading(0),
+ _editorId(-1),
+ _editorChangeWarning(false)
{
LOG_INF("Document ctor for [" << _docKey <<
"] url [" << _url << "] on child [" << _jailId <<
@@ -561,6 +563,10 @@ public:
auto session = std::make_shared<ChildSession>(sessionId, _jailId, *this);
_sessions.emplace(sessionId, session);
+ int viewId = session->getViewId();
+ _lastUpdatedAt[viewId] = std::chrono::steady_clock::now();
+ _speedCount[viewId] = 0;
+
LOG_DBG("Sessions: " << _sessions.size());
return true;
}
@@ -1098,6 +1104,11 @@ private:
return _tileQueue;
}
+ int getEditorId() override
+ {
+ return _editorId;
+ }
+
/// Notify all views of viewId and their associated usernames
void notifyViewInfo() override
{
@@ -1153,6 +1164,51 @@ private:
sendTextFrame("client-all " + msg);
}
+ void updateEditorSpeeds(int id, int speed) override
+ {
+ int maxSpeed = -1, fastestUser = -1;
+
+ auto now = std::chrono::steady_clock::now();
+ _lastUpdatedAt[id] = now;
+ _speedCount[id] = speed;
+
+ for (const auto& it : _sessions)
+ {
+ const auto session = it.second;
+ int sessionId = session->getViewId();
+
+ auto duration = (_lastUpdatedAt[id] - now);
+ auto durationInMs = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ if (_speedCount[sessionId] != 0 && durationInMs > 5000)
+ {
+ _speedCount[sessionId] = session->getSpeed();
+ _lastUpdatedAt[sessionId] = now;
+ }
+ if (_speedCount[sessionId] > maxSpeed)
+ {
+ maxSpeed = _speedCount[sessionId];
+ fastestUser = sessionId;
+ }
+ }
+ // 0 for preventing selection of the first always
+ // 1 for preventing the new users from directly beoming the editors
+ if (_editorId != fastestUser && (maxSpeed != 0 || maxSpeed != 1)) {
+ if (!_editorChangeWarning && _editorId != -1)
+ {
+ _editorChangeWarning = true;
+ }
+ else
+ {
+ _editorChangeWarning = false;
+ _editorId = fastestUser;
+ for (const auto& it : _sessions)
+ it.second->sendTextFrame("editor: " + std::to_string(_editorId));
+ }
+ }
+ else
+ _editorChangeWarning = false;
+ }
+
private:
// Get the color value for all author names from the core
@@ -1351,6 +1407,9 @@ private:
if (size == disconnect.size() &&
strncmp(data, disconnect.data(), disconnect.size()) == 0)
{
+ if(session->getViewId() == _editorId) {
+ _editorId = -1;
+ }
LOG_DBG("Removing ChildSession [" << sessionId << "].");
_sessions.erase(it);
const auto count = _sessions.size();
@@ -1431,7 +1490,7 @@ private:
LOG_DBG("Thread started.");
- // Update memory stats every 5 seconds.
+ // Update memory stats and editor every 5 seconds.
const auto memStatsPeriodMs = 5000;
auto lastMemStatsTime = std::chrono::steady_clock::now();
sendTextFrame(Util::getMemoryStats(ProcSMapsFile));
@@ -1443,14 +1502,13 @@ private:
const TileQueue::Payload input = _tileQueue->get(POLL_TIMEOUT_MS * 2);
if (input.empty())
{
- const auto duration = (std::chrono::steady_clock::now() - lastMemStatsTime);
- const auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ auto duration = (std::chrono::steady_clock::now() - lastMemStatsTime);
+ auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
if (durationMs > memStatsPeriodMs)
{
sendTextFrame(Util::getMemoryStats(ProcSMapsFile));
lastMemStatsTime = std::chrono::steady_clock::now();
}
-
continue;
}
@@ -1616,9 +1674,13 @@ private:
std::condition_variable _cvLoading;
std::atomic_size_t _isLoading;
+ int _editorId;
+ bool _editorChangeWarning;
std::map<int, std::unique_ptr<CallbackDescriptor>> _viewIdToCallbackDescr;
std::map<std::string, std::shared_ptr<ChildSession>> _sessions;
+ std::map<int, std::chrono::steady_clock::time_point> _lastUpdatedAt;
+ std::map<int, int> _speedCount;
/// For showing disconnected user info in the doc repair dialog.
std::map<int, UserInfo> _sessionUserInfo;
Poco::Thread _callbackThread;
diff --git a/loleaflet/dist/editor.css b/loleaflet/dist/editor.css
index be24c214..5e3b0ea0 100644
--- a/loleaflet/dist/editor.css
+++ b/loleaflet/dist/editor.css
@@ -1,3 +1,21 @@
+#userlist_table {
+ width: 100%;
+}
.selected-user {
background-color: darkgrey;
+}
+#editor-btn {
+ padding: 3px 5px;
+ max-width: 120px;
+ text-align: center;
+ vertical-align: middle;
+ width: 100%;
+ font-size: 14px;
+}
+#editor-btn #follow-checkbox{
+ margin: 3px;
+}
+#currently-msg{
+ text-align: center;
+ padding: 3px 5px;
}
\ No newline at end of file
diff --git a/loleaflet/dist/toolbar/toolbar.js b/loleaflet/dist/toolbar/toolbar.js
index b615af4d..e33ad8f7 100644
--- a/loleaflet/dist/toolbar/toolbar.js
+++ b/loleaflet/dist/toolbar/toolbar.js
@@ -678,7 +678,16 @@ $(function () {
{type: 'html', id: 'right'},
{type: 'html', id: 'modifiedstatuslabel', html: '<div id="modifiedstatuslabel" class="loleaflet-font"></div>'},
{type: 'break', id: 'modifiedstatuslabelbreak'},
- {type: 'drop', id: 'userlist', text: _('No users'), html: '<div id="userlist_container"><table id="userlist_table"><tbody></tbody></table></div>' },
+ {type: 'drop', id: 'userlist', text: _('No users'), html: '<div id="userlist_container"><table id="userlist_table"><tbody></tbody></table>' +
+ '<hr><table class="loleaflet-font" id="editor-btn">' +
+ '<tr>' +
+ '<td><input type="checkbox" name="alwaysFollow" id="follow-checkbox" onclick="editorUpdate(event)"></td>' +
+ '<td>Always follow the editor</td>' +
+ '</tr>' +
+ '</table>' +
+ '<p id="currently-msg">Current - <b><span id="current-editor">Dewan</span></b></p>' +
+ '</div>'
+ },
{type: 'break', id: 'userlistbreak'},
{type: 'button', id: 'prev', img: 'prev', hint: _('Previous page')},
{type: 'button', id: 'next', img: 'next', hint: _('Next page')},
@@ -690,6 +699,19 @@ $(function () {
],
onClick: function (e) {
if (e.item.id === 'userlist') {
+ setTimeout(function() {
+ var cBox = $('#follow-checkbox')[0];
+ var docLayer = map._docLayer;
+ var editorId = docLayer._editorId;
+
+ if (cBox)
+ cBox.checked = docLayer._followEditor;
+
+ if (docLayer.editorId !== -1 && map._viewInfo[editorId])
+ $('#current-editor').text(map._viewInfo[editorId].username);
+ else
+ $('#currently-msg').hide();
+ }, 100);
return;
}
onClick(e.target, e.item, e.subItem);
@@ -1576,15 +1598,24 @@ map.on('keydown', function (e) {
}
});
-function onUseritemClicked(e) {
+function goToViewId(id) {
var docLayer = map._docLayer;
- var viewId = parseInt(e.currentTarget.id.replace('user-', ''));
+
+ if (id === -1)
+ return;
if (map.getDocType() === 'spreadsheet') {
- docLayer.goToCellViewCursor(viewId);
+ docLayer.goToCellViewCursor(id);
} else if (map.getDocType() === 'text' || map.getDocType() === 'presentation') {
- docLayer.goToViewCursor(viewId);
+ docLayer.goToViewCursor(id);
}
+}
+
+function onUseritemClicked(e) {
+ var docLayer = map._docLayer;
+ var viewId = parseInt(e.currentTarget.id.replace('user-', ''));
+
+ goToViewId(viewId);
if (viewId === map._docLayer._viewId) {
$('#tb_toolbar-down_item_userlist').w2overlay('');
@@ -1600,6 +1631,31 @@ function onUseritemClicked(e) {
selectUser(viewId);
}
+function editorUpdate(e) {
+ var docLayer = map._docLayer;
+
+ if (e.target.checked) {
+ var editorId = docLayer._editorId;
+ var userlistItem = w2ui['toolbar-down'].get('userlist');
+
+ docLayer._followUser = false;
+ docLayer._followEditor = true;
+ if (editorId !== -1 && editorId !== docLayer.viewId) {
+ goToViewId(editorId);
+ docLayer._followThis = editorId;
+ }
+
+ $('.selected-user').removeClass('selected-user');
+ if ($(userlistItem.html).find('.selected-user').length !== 0)
+ userlistItem.html = $(userlistItem.html).find('.selected-user').removeClass('selected-user').parent().parent().parent()[0].outerHTML;
+ $('#tb_toolbar-down_item_userlist').w2overlay('');
+ }
+ else {
+ docLayer._followEditor = false;
+ docLayer._followThis = -1;
+ }
+}
+
function selectUser(viewId) {
var userlistItem = w2ui['toolbar-down'].get('userlist');
userlistItem.html = $(userlistItem.html).find('#user-' + viewId).addClass('selected-user').parent().parent().parent()[0].outerHTML;
@@ -1703,6 +1759,11 @@ map.on('removeview', function(e) {
updateUserListCount();
});
+map.on('updateEditorName', function(e) {
+ $('#currently-msg').show()
+ $('#current-editor').text(e.username);
+});
+
map.on('setFollowOff', function(e) {
var docLayer = map._docLayer;
var viewId = docLayer._followThis;
diff --git a/loleaflet/src/layer/marker/Cursor.js b/loleaflet/src/layer/marker/Cursor.js
index 0a5e622d..792a1348 100644
--- a/loleaflet/src/layer/marker/Cursor.js
+++ b/loleaflet/src/layer/marker/Cursor.js
@@ -68,7 +68,8 @@ L.Cursor = L.Layer.extend({
if (this._cursorHeader) {
L.DomUtil.setStyle(this._cursorHeader, 'visibility', 'visible');
- setTimeout(L.bind(function() {
+ clearTimeout(this._blinkTimeout);
+ this._blinkTimeout = setTimeout(L.bind(function() {
L.DomUtil.setStyle(this._cursorHeader, 'visibility', 'hidden');
}, this), this.options.headerTimeout);
}
@@ -81,7 +82,8 @@ L.Cursor = L.Layer.extend({
this._cursorHeader.innerHTML = this.options.headerName;
- setTimeout(L.bind(function() {
+ clearTimeout(this._blinkTimeout);
+ this._blinkTimeout = setTimeout(L.bind(function() {
L.DomUtil.setStyle(this._cursorHeader, 'visibility', 'hidden');
}, this), this.options.headerTimeout);
}
diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 9c20b055..d909ad8f 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -143,6 +143,7 @@ L.TileLayer = L.GridLayer.extend({
'tiletwipheight=' + this.options.tileHeightTwips;
this._followThis = -1;
+ this._editorId = -1;
this._followUser = false;
this._followEditor = false;
@@ -459,6 +460,9 @@ L.TileLayer = L.GridLayer.extend({
else if (textMsg.startsWith('graphicviewselection:')) {
this._onGraphicViewSelectionMsg(textMsg);
}
+ else if (textMsg.startsWith('editor:')) {
+ this._updateEditor(textMsg);
+ }
},
toggleTileDebugMode: function() {
@@ -723,6 +727,21 @@ L.TileLayer = L.GridLayer.extend({
this._onUpdateCursor();
},
+ _updateEditor: function(textMsg) {
+ textMsg = textMsg.substring('editor:'.length + 1);
+ var editorId = parseInt(textMsg);
+ var docLayer = this._map._docLayer;
+
+ docLayer._editorId = editorId;
+
+ if (docLayer._followEditor) {
+ docLayer._followThis = editorId;
+ }
+
+ if (this._map._viewInfo[editorId])
+ this._map.fire('updateEditorName', {username: this._map._viewInfo[editorId].username})
+ },
+
_onInvalidateViewCursorMsg: function (textMsg) {
textMsg = textMsg.substring('invalidateviewcursor:'.length + 1);
var obj = JSON.parse(textMsg);
diff --git a/test/WhiteBoxTests.cpp b/test/WhiteBoxTests.cpp
index 3493bed1..7079c60a 100644
--- a/test/WhiteBoxTests.cpp
+++ b/test/WhiteBoxTests.cpp
@@ -351,6 +351,15 @@ public:
{
}
+ void updateEditorSpeeds(int, int) override
+ {
+ }
+
+ int getEditorId() override
+ {
+ return -1;
+ }
+
std::map<int, UserInfo> getViewInfo() override
{
return {};
More information about the Libreoffice-commits
mailing list