From 8ce397b758e3eebc76119f19659ce47b73b6e1ad Mon Sep 17 00:00:00 2001 From: Ashod Nakashian Date: Tue, 25 Jun 2019 22:42:15 -0400 Subject: [PATCH] clipboard: Copying of complex data with detection Complex data is now flagged based on Core's feedback and/or the size of the payload. Download now works as expected and copying to the clipboard is also functional. Change-Id: I7405517e3a6afcc4c8f5843130476578c1ff06f6 --- .../include/LibreOfficeKit/LibreOfficeKit.hxx | 50 +++++++++++++++++++ kit/ChildSession.cpp | 28 ++++++++++- .../src/control/Control.DownloadProgress.js | 16 ++++-- loleaflet/src/layer/tile/TileLayer.js | 13 +---- loleaflet/src/map/Clipboard.js | 27 ++++------ 5 files changed, 98 insertions(+), 36 deletions(-) diff --git a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx index 47983e68f..0d1c6e94d 100644 --- a/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx +++ b/bundled/include/LibreOfficeKit/LibreOfficeKit.hxx @@ -351,6 +351,56 @@ public: return mpDoc->pClass->getTextSelection(mpDoc, pMimeType, pUsedMimeType); } + /** + * Gets the type of the selected content. + * + * @return an element of the LibreOfficeKitSelectionType enum. + */ + int getSelectionType() + { + return mpDoc->pClass->getSelectionType(mpDoc); + } + + /** + * Gets the content on the clipboard for the current view as a series of binary streams. + * + * NB. returns a complete set of possible selection types if nullptr is passed for pMimeTypes. + * + * @param pMimeTypes passes in a nullptr terminated list of mime types to fetch + * @param pOutCount returns the size of the other @pOut arrays + * @param pOutMimeTypes returns an array of mime types + * @param pOutSizes returns the size of each pOutStream + * @param pOutStreams the content of each mime-type, of length in @pOutSizes + * + * @returns: true on success, false on error. + */ + bool getClipboard(const char **pMimeTypes, + size_t *pOutCount, + char ***pOutMimeTypes, + size_t **pOutSizes, + char ***pOutStreams) + { + return mpDoc->pClass->getClipboard(mpDoc, pMimeTypes, pOutCount, pOutMimeTypes, pOutSizes, pOutStreams); + } + + /** + * Populates the clipboard for this view with multiple types of content. + * + * @param nInCount the number of types to paste + * @param pInMimeTypes array of mime type strings + * @param pInSizes array of sizes of the data to paste + * @param pInStreams array containing the data of the various types + * + * @return if the supplied data was populated successfully. + */ + bool setClipboard(const size_t nInCount, + const char **pInMimeTypes, + const size_t *pInSizes, + const char **pInStreams) + { + return mpDoc->pClass->setClipboard(mpDoc, nInCount, pInMimeTypes, pInSizes, pInStreams); + } + /** * Pastes content at the current cursor position. * diff --git a/kit/ChildSession.cpp b/kit/ChildSession.cpp index 93387eef6..07303de6e 100644 --- a/kit/ChildSession.cpp +++ b/kit/ChildSession.cpp @@ -917,7 +917,31 @@ bool ChildSession::getTextSelection(const char* /*buffer*/, int /*length*/, cons return false; } - sendTextFrame("textselectioncontent: " + getTextSelectionInternal(mimeType)); + if (getLOKitDocument()->getDocumentType() != LOK_DOCTYPE_TEXT) + { + const std::string selection = getTextSelectionInternal(mimeType); + if (selection.size() >= 1024 * 1024) // Don't return huge data. + { + // Flag complex data so the client will download async. + sendTextFrame("complexselection:"); + return true; + } + + sendTextFrame("textselectioncontent: " + selection); + return true; + } + + std::string selection; + const int selectionType = getLOKitDocument()->getSelectionType(); + if (selectionType == LOK_SELTYPE_LARGE_TEXT || selectionType == LOK_SELTYPE_COMPLEX || + (selection = getTextSelectionInternal(mimeType)).size() >= 1024 * 1024) // Don't return huge data. + { + // Flag complex data so the client will download async. + sendTextFrame("complexselection:"); + return true; + } + + sendTextFrame("textselectioncontent: " + selection); return true; } @@ -996,7 +1020,7 @@ bool ChildSession::setClipboard(const char* buffer, int length, const std::vecto // data.dumpState(std::cerr); size_t nInCount = data.size(); - size_t pInSizes[nInCount] = { 0, }; + size_t pInSizes[nInCount]; const char *pInMimeTypes[nInCount]; const char *pInStreams[nInCount]; diff --git a/loleaflet/src/control/Control.DownloadProgress.js b/loleaflet/src/control/Control.DownloadProgress.js index c0aeaa169..63369db26 100644 --- a/loleaflet/src/control/Control.DownloadProgress.js +++ b/loleaflet/src/control/Control.DownloadProgress.js @@ -53,6 +53,7 @@ L.Control.DownloadProgress = L.Control.extend({ confirmCopy.style.alignment = 'center'; confirmCopy.style.height = 20 + 'px'; L.DomEvent.on(confirmCopy, 'click', this._onConfirmCopyAction, this); + this._closed = false; }, show: function () { @@ -62,6 +63,10 @@ L.Control.DownloadProgress = L.Control.extend({ this._container.style.visibility = ''; }, + isClosed: function () { + return this._closed; + }, + setURI: function (uri) { // set up data uri to be downloaded this._uri = uri; @@ -112,8 +117,8 @@ L.Control.DownloadProgress = L.Control.extend({ this._content.appendChild(this._confirmPasteButton); }, - _onConfirmCopyAction: function (ev) { - this._map._clip.copy(ev); + _onConfirmCopyAction: function () { + this._map._clip.filterExecCopyPaste('.uno:Copy'); this._map._clip._downloadProgress = null; this._onClose(); }, @@ -127,6 +132,7 @@ L.Control.DownloadProgress = L.Control.extend({ this._content.removeChild(this._progress); this._map.focus(); this.remove(); + this._closed = true; }, _download: function () { @@ -138,12 +144,12 @@ L.Control.DownloadProgress = L.Control.extend({ // annoying async parse of the blob ... var reader = new FileReader(); reader.onload = function() { - console.log('async clipboard parse done: ' + text.substring(0, 256)) var text = reader.result; + console.log('async clipboard parse done: ' + text.substring(0, 256)) var idx = text.indexOf(' 0) - text = text.substring(idx, text.length()); - that._map._clip.setSelection(text); + text = text.substring(idx, text.length); + that._map._clip.setTextSelectionContent(text); // TODO: now swap to the 'copy' button (?) }; // TODO: failure to parse ? ... diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js index e779ac0df..148eb6500 100644 --- a/loleaflet/src/layer/tile/TileLayer.js +++ b/loleaflet/src/layer/tile/TileLayer.js @@ -585,7 +585,7 @@ L.TileLayer = L.GridLayer.extend({ // message is received from lowsd, *then* a 'celladdress' message. var address = textMsg.substring(13); if (!this._map['wopi'].DisableCopy) { - this._map._clip.setSelection(this._lastFormula); + this._map._clip.setTextSelectionContent(this._lastFormula); } this._map.fire('celladdress', {address: address}); }, @@ -2636,17 +2636,6 @@ L.TileLayer = L.GridLayer.extend({ } }, - _selectionType: function() { - if (this._graphicSelection !== null && - !this._isEmptyRectangle(this._graphicSelection)) { - return 'complex'; // FIXME: Ash - complex ... - } else if (this._selections.getLayers().length > 0) { - return 'simpletext'; - } else { - return null; - } - }, - _onCopy: function (e) { e = e.originalEvent; this._map._clip.copy(e); diff --git a/loleaflet/src/map/Clipboard.js b/loleaflet/src/map/Clipboard.js index e2ffc8d53..138b38bb2 100644 --- a/loleaflet/src/map/Clipboard.js +++ b/loleaflet/src/map/Clipboard.js @@ -13,6 +13,7 @@ L.Clipboard = L.Class.extend({ initialize: function(map) { this._map = map; this._selectionContent = ''; + this._selectionType = null; this._accessKey = [ '', '' ]; this._clipboardSerial = 0; // incremented on each operation @@ -273,12 +274,11 @@ L.Clipboard = L.Class.extend({ }, populateClipboard: function(ev) { - var t = this._map._docLayer._selectionType(); var text; - if (t === null) { + if (this._selectionType === null) { console.log('Copy/Cut with no selection!'); text = this.getStubHtml(); - } else if (t === 'complex') { + } else if (this._selectionType === 'complex') { console.log('Copy/Cut with complex/graphical selection'); text = this.getStubHtml(); this._onDownloadOnLargeCopyPaste(); @@ -468,26 +468,19 @@ L.Clipboard = L.Class.extend({ clearSelection: function() { this._selectionContent = ''; - }, - - setSelection: function(content) { - this._selectionContent = content; + this._selectionType = null; }, // textselectioncontent: message setTextSelectionContent: function(text) { - this.setSelection(text); + this._selectionType = 'text'; + this._selectionContent = text; }, // complexselection: message - onComplexSelection: function (text) { - // Put in the clipboard a helpful explanation of what the user should do. - // Currently we don't have a payload, though we might in the future - text = _('Please use the following link to download the selection from you document and paste into other applications on your device') - + ': '; //FIXME: MISSING URL - this.setSelection(text); - - //TODO: handle complex selection download. + onComplexSelection: function (/*text*/) { + // Mark this selection as complex. + this._selectionType = 'complex'; }, _startProgress: function() { @@ -501,7 +494,7 @@ L.Clipboard = L.Class.extend({ }, _onDownloadOnLargeCopyPaste: function () { - if (!this._downloadProgress) { + if (!this._downloadProgress || this._downloadProgress.isClosed()) { this._warnFirstLargeCopyPaste(); this._startProgress(); }