loleaflet/src/control/Control.ColumnHeader.js | 181 +++++++++------- loleaflet/src/control/Control.Header.js | 294 +++++++++++++++++++++----- loleaflet/src/control/Control.RowHeader.js | 166 ++++++++------ 3 files changed, 435 insertions(+), 206 deletions(-)
New commits: commit e839abf5376713fb26bf8dc47fb0d31efcabb727 Author: Marco Cecchetti <marco.cecche...@collabora.com> Date: Thu Nov 9 18:58:09 2017 +0100 loleaflet: sc: handle header data subdiveded in ranges Change-Id: I54b8bc80af91414d8a804e54a478a2eb452510e3 Reviewed-on: https://gerrit.libreoffice.org/45417 Reviewed-by: Jan Holesovsky <ke...@collabora.com> Tested-by: Jan Holesovsky <ke...@collabora.com> diff --git a/loleaflet/src/control/Control.ColumnHeader.js b/loleaflet/src/control/Control.ColumnHeader.js index 27765ec7..264fea4d 100644 --- a/loleaflet/src/control/Control.ColumnHeader.js +++ b/loleaflet/src/control/Control.ColumnHeader.js @@ -57,8 +57,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ callback: function(key, options) { var index = colHeaderObj._lastMouseOverIndex; if (index) { - var colAlpha = colHeaderObj._data[index].text; - colHeaderObj.insertColumn.call(colHeaderObj, colAlpha); + colHeaderObj.insertColumn.call(colHeaderObj, index); } } }, @@ -67,8 +66,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ callback: function(key, options) { var index = colHeaderObj._lastMouseOverIndex; if (index) { - var colAlpha = colHeaderObj._data[index].text; - colHeaderObj.deleteColumn.call(colHeaderObj, colAlpha); + colHeaderObj.deleteColumn.call(colHeaderObj, index); } } }, @@ -77,8 +75,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ callback: function(key, options) { var index = colHeaderObj._lastMouseOverIndex; if (index) { - var colAlpha = colHeaderObj._data[index].text; - colHeaderObj.optimalWidth.call(colHeaderObj, colAlpha); + colHeaderObj.optimalWidth.call(colHeaderObj, index); } } }, @@ -87,8 +84,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ callback: function(key, options) { var index = colHeaderObj._lastMouseOverIndex; if (index) { - var colAlpha = colHeaderObj._data[index].text; - colHeaderObj.hideColumn.call(colHeaderObj, colAlpha); + colHeaderObj.hideColumn.call(colHeaderObj, index); } } }, @@ -97,8 +93,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ callback: function(key, options) { var index = colHeaderObj._lastMouseOverIndex; if (index) { - var colAlpha = colHeaderObj._data[index].text; - colHeaderObj.showColumn.call(colHeaderObj, colAlpha); + colHeaderObj.showColumn.call(colHeaderObj, index); } } } @@ -107,50 +102,50 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }); }, - optimalWidth: function(colAlpha) { + optimalWidth: function(index) { if (!this._dialog) { this._dialog = L.control.metricInput(this._onDialogResult, this, this._map._docLayer.twipsToHMM(this._map._docLayer.STD_EXTRA_WIDTH), {title: _('Optimal Column Width')}); } if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectColumn(colAlpha, 0); + this._selectColumn(index, 0); } this._dialog.addTo(this._map); this._map.enable(false); this._dialog.show(); }, - insertColumn: function(colAlpha) { + insertColumn: function(index) { // First select the corresponding column because // .uno:InsertColumn doesn't accept any column number // as argument and just inserts before the selected column if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectColumn(colAlpha, 0); + this._selectColumn(index, 0); } this._map.sendUnoCommand('.uno:InsertColumns'); this._updateColumnHeader(); }, - deleteColumn: function(colAlpha) { + deleteColumn: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectColumn(colAlpha, 0); + this._selectColumn(index, 0); } this._map.sendUnoCommand('.uno:DeleteColumns'); this._updateColumnHeader(); }, - hideColumn: function(colAlpha) { + hideColumn: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectColumn(colAlpha, 0); + this._selectColumn(index, 0); } this._map.sendUnoCommand('.uno:HideColumn'); this._updateColumnHeader(); }, - showColumn: function(colAlpha) { + showColumn: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectColumn(colAlpha, 0); + this._selectColumn(index, 0); } this._map.sendUnoCommand('.uno:ShowColumn'); this._updateColumnHeader(); @@ -171,50 +166,44 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }, _onUpdateSelection: function (e) { - var data = this._data; - if (!data) - return; var start = e.start.x; var end = e.end.x; - var twips; if (start !== -1) { - twips = new L.Point(start, start); - start = Math.round(data.converter.call(data.context, twips).x); + start = this._twipsToPixels(start); } if (end !== -1) { - twips = new L.Point(end, end); - end = Math.round(data.converter.call(data.context, twips).x); + end = this._twipsToPixels(end); } - this.updateSelection(data, start, end); + this.updateSelection(this._data, start, end); }, _onUpdateCurrentColumn: function (e) { - var data = this._data; - if (!data) - return; var x = e.x; if (x !== -1) { - var twips = new L.Point(x, x); - x = Math.round(data.converter.call(data.context, twips).x); + x = this._twipsToPixels(x); } - this.updateCurrent(data, x); + this.updateCurrent(this._data, x); }, _updateColumnHeader: function () { this._map.fire('updaterowcolumnheaders', {x: this._map._getTopLeftPoint().x, y: 0, offset: {x: undefined, y: 0}}); }, - drawHeaderEntry: function (index, isOver) { - if (!index || index <= 0 || index >= this._data.length) + drawHeaderEntry: function (entry, isOver, isHighlighted) { + if (!entry) return; var ctx = this._canvasContext; - var content = this._data[index].text; - var start = this._data[index - 1].pos - this._leftOffset; - var end = this._data[index].pos - this._leftOffset; + var content = this._colIndexToAlpha(entry.index + this._leftmostColumn); + var start = entry.pos - entry.size - this._leftOffset; + var end = entry.pos - this._leftOffset; var width = end - start; var height = this._headerCanvas.height; - var isHighlighted = this._data[index].selected; + + if (isHighlighted !== true && isHighlighted !== false) { + isHighlighted = this.isHighlighted(entry.index); + } + if (width <= 0) return; @@ -249,22 +238,23 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }, getHeaderEntryBoundingClientRect: function (index) { - if (!index) - index = this._mouseOverIndex; // use last mouse over position + var entry = this._mouseOverEntry; + if (index) + entry = this._data.get(index); - if (!index || !this._data[index]) + if (!entry) return; var rect = this._headerCanvas.getBoundingClientRect(); - var colStart = this._data[index - 1].pos + this._position; - var colEnd = this._data[index].pos + this._position; + var colStart = entry.pos - entry.size + this._position; + var colEnd = entry.pos + this._position; var left = rect.left + colStart; var right = rect.left + colEnd; var top = rect.top; var bottom = rect.bottom; - return { left: left, right: right, top: top, bottom: bottom }; + return {left: left, right: right, top: top, bottom: bottom}; }, viewRowColumnHeaders: function (e) { @@ -274,31 +264,57 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }, fillColumns: function (columns, converter, context) { - var iterator, twip, width; + if (columns.length < 2) + return; - this._data = new Array(columns.length); - this._data.converter = converter; - this._data.context = context; + var entry, index, iterator, pos, width; var canvas = this._headerCanvas; canvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width')); canvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height')); - this._canvasContext.clearRect(0, 0, canvas.width, canvas.height); - var leftmostOffset = new L.Point(columns[0].size, columns[0].size); + // update first header index and reset no more valid variables this._leftmostColumn = parseInt(columns[0].text); - this._leftOffset = Math.round(converter.call(context, leftmostOffset).x); + this._current = -1; // no more valid + this._selection.start = this._selection.end = -1; // no more valid + this._mouseOverEntry = null; + this._lastMouseOverIndex = undefined; + + // create header data handler instance + this._data = new L.Control.Header.DataImpl(); + + // setup conversion routine + this.converter = L.Util.bind(converter, context); + this._data.converter = L.Util.bind(this._twipsToPixels, this); + + var startOffsetTw = parseInt(columns[0].size); + this._leftOffset = this._twipsToPixels(startOffsetTw); + + this._data.pushBack(0, {pos: startOffsetTw, size: 0}); + var prevPos = startOffsetTw; + var nextIndex = parseInt(columns[1].text); + var last = columns.length - 1; + for (iterator = 1; iterator < last; iterator++) { + index = nextIndex; + pos = parseInt(columns[iterator].size); + nextIndex = parseInt(columns[iterator+1].text); + width = pos - prevPos; + prevPos = Math.round(pos + width * (nextIndex - index - 1)); + index = index - this._leftmostColumn; + entry = {pos: pos, size: width}; + this._data.pushBack(index, entry); + } - this._data[0] = { pos: this._leftOffset, text: '', selected: false }; + // setup last header entry + pos = parseInt(columns[last].size); + this._data.pushBack(nextIndex - this._leftmostColumn, {pos: pos, size: pos - prevPos}); - for (iterator = 1; iterator < columns.length; iterator++) { - twip = new L.Point(columns[iterator].size, columns[iterator].size); - this._data[iterator] = { pos: Math.round(converter.call(context, twip).x), text: columns[iterator].text, selected: false }; - width = this._data[iterator].pos - this._data[iterator - 1].pos; - if (width > 0) { - this.drawHeaderEntry(iterator, false); - } + // draw header + entry = this._data.getFirst(); + while (entry) { + this.drawHeaderEntry(entry, false); + entry = this._data.getNext(); } this.mouseInit(canvas); @@ -320,13 +336,26 @@ L.Control.ColumnHeader = L.Control.Header.extend({ return res; }, - _selectColumn: function(colAlpha, modifier) { - var colNumber = this._colAlphaToNumber(colAlpha); + _colIndexToAlpha: function(columnNumber) { + var offset = 'A'.charCodeAt(); + var dividend = columnNumber; + var columnName = ''; + var modulo; + + while (dividend > 0) { + modulo = (dividend - 1) % 26; + columnName = String.fromCharCode(offset + modulo) + columnName; + dividend = Math.floor((dividend - modulo) / 26); + } + + return columnName; + }, + _selectColumn: function(colNumber, modifier) { var command = { Col: { type: 'unsigned short', - value: parseInt(colNumber - 1) + value: colNumber - 1 }, Modifier: { type: 'unsigned short', @@ -338,10 +367,10 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }, _onHeaderClick: function (e) { - if (!this._mouseOverIndex) + if (!this._mouseOverEntry) return; - var colAlpha = this._data[this._mouseOverIndex].text; + var col = this._mouseOverEntry.index + this._leftmostColumn; var modifier = 0; if (e.shiftKey) { @@ -351,7 +380,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ modifier += this._map.keyboard.keyModifier.ctrl; } - this._selectColumn(colAlpha, modifier); + this._selectColumn(col, modifier); }, _onCornerHeaderClick: function() { @@ -403,14 +432,14 @@ L.Control.ColumnHeader = L.Control.Header.extend({ var end = new L.Point(e.clientX + offset.x, e.clientY); var distance = this._map._docLayer._pixelsToTwips(end.subtract(start)); - if (this._mouseOverIndex) { - var clickedColumn = this._data[this._mouseOverIndex]; - var width = clickedColumn.pos - this._data[this._mouseOverIndex - 1]; - var column = this._mouseOverIndex + this._leftmostColumn; + var clickedColumn = this._mouseOverEntry; + if (clickedColumn) { + var width = clickedColumn.size; + var column = clickedColumn.index + this._leftmostColumn; - if (this._data[this._mouseOverIndex + 1] - && this._data[this._mouseOverIndex + 1].pos === clickedColumn.pos) { + if (this._data.isZeroSize(clickedColumn.index + 1)) { column += 1; + width = 0; } if (width !== distance.x) { @@ -436,11 +465,11 @@ L.Control.ColumnHeader = L.Control.Header.extend({ onDragClick: function (item, clicks, e) { this._map.removeLayer(this._vertLine); - if (!this._mouseOverIndex) + if (!this._mouseOverEntry) return; if (clicks === 2) { - var column = this._mouseOverIndex + this._leftmostColumn; + var column = this._mouseOverEntry.index + this._leftmostColumn; var command = { Col: { type: 'unsigned short', diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js index a94663a2..d7689ee0 100644 --- a/loleaflet/src/control/Control.Header.js +++ b/loleaflet/src/control/Control.Header.js @@ -8,11 +8,13 @@ L.Control.Header = L.Control.extend({ }, initialize: function () { + this.converter = null; + this._headerCanvas = null; this._clicks = 0; this._current = -1; this._selection = {start: -1, end: -1}; - this._mouseOverIndex = undefined; + this._mouseOverEntry = null; this._lastMouseOverIndex = undefined; this._hitResizeArea = false; @@ -77,65 +79,82 @@ L.Control.Header = L.Control.extend({ L.DomEvent.on(element, 'mousedown', this._onMouseDown, this); }, - select: function (data, index) { - if (!data[index]) - return; - data[index].selected = true; - this.drawHeaderEntry(index, false); + select: function (entry) { + this.drawHeaderEntry(entry, /*isOver=*/false, /*isHighlighted=*/true); }, - unselect: function (data, index) { - if (!data[index]) - return; - data[index].selected = false; - this.drawHeaderEntry(index, false); + unselect: function (entry) { + this.drawHeaderEntry(entry, /*isOver=*/false, /*isHighlighted=*/false); + }, + + isHighlighted: function (index) { + if (this._selection.start === -1 && this._selection.end === -1) { + return index === this._current; + } + return (this._selection.start <= index && index <= this._selection.end); }, clearSelection: function (data) { if (this._selection.start === -1 && this._selection.end === -1) return; - var start = (this._selection.start === -1) ? 0 : this._selection.start; + var start = (this._selection.start < 1) ? 1 : this._selection.start; var end = this._selection.end + 1; - for (var iterator = start; iterator < end; iterator++) { - this.unselect(data, iterator); + + var entry = data.getAt(start); + + while (entry && entry.index < end) { + this.unselect(entry); + entry = data.getNext(start); } this._selection.start = this._selection.end = -1; // after clearing selection, we need to select the header entry for the current cursor position, // since we can't be sure that the selection clearing is due to click on a cell // different from the one where the cursor is already placed - this.select(data, this._current); + this.select(data.get(this._current)); }, updateSelection: function(data, start, end) { - if (!data) + if (!data || data.isEmpty()) return; var x0 = 0, x1 = 0; var itStart = -1, itEnd = -1; var selected = false; - var iterator = 0; - for (var len = data.length; iterator < len; iterator++) { - x0 = (iterator > 0 ? data[iterator - 1].pos : 0); - x1 = data[iterator].pos; - // 'start < x1' not '<=' or we get highlighted also the `start-row - 1` and `start-column - 1` headers + + // if the start selection position is above/on the left of the first header entry, + // but the end selection position is below/on the right of it + // then we set the start selected entry to the first header entry. + var entry = data.getFirst(); + if (entry) { + x0 = entry.pos - entry.size; + if (start < x0 && end > x0) { + selected = true; + itStart = 1; + } + } + + while (entry) { + x0 = entry.pos - entry.size; + x1 = entry.pos; if (x0 <= start && start < x1) { selected = true; - itStart = iterator; + itStart = entry.index; } if (selected) { - this.select(data, iterator); + this.select(entry); } if (x0 <= end && end <= x1) { - itEnd = iterator; + itEnd = entry.index; break; } + entry = data.getNext(); } // if end is greater than the last fetched header position set itEnd to the max possible value // without this hack selecting a whole row and then a whole column (or viceversa) leads to an incorrect selection if (itStart !== -1 && itEnd === -1) { - itEnd = data.length - 1; + itEnd = data.getLength() - 1; } // we need to unselect the row (column) header entry for the current cell cursor position @@ -143,17 +162,22 @@ L.Control.Header = L.Control.extend({ // does not start by clicking on a cell if (this._current !== -1 && itStart !== -1 && itEnd !== -1) { if (this._current < itStart || this._current > itEnd) { - this.unselect(data, this._current); + this.unselect(data.get(this._current)); } } + if (this._selection.start !== -1 && itStart !== -1 && itStart > this._selection.start) { - for (iterator = this._selection.start; iterator < itStart; iterator++) { - this.unselect(data, iterator); + entry = data.getAt(this._selection.start); + while (entry && entry.index < itStart) { + this.unselect(entry); + entry = data.getNext(); } } if (this._selection.end !== -1 && itEnd !== -1 && itEnd < this._selection.end) { - for (iterator = itEnd + 1; iterator <= this._selection.end; iterator++) { - this.unselect(data, iterator); + entry = data.getAt(itEnd + 1); + while (entry && entry.index <= this._selection.end) { + this.unselect(entry); + entry = data.getNext(); } } this._selection.start = itStart; @@ -161,30 +185,33 @@ L.Control.Header = L.Control.extend({ }, updateCurrent: function (data, start) { - if (!data) + if (!data || data.isEmpty()) return; + if (start < 0) { - this.unselect(data, this._current); + this.unselect(data.get(this._current)); this._current = -1; return; } var x0 = 0, x1 = 0; - for (var iterator = 1, len = data.length; iterator < len; iterator++) { - x0 = (iterator > 0 ? data[iterator - 1].pos : 0); - x1 = data[iterator].pos; + var entry = data.getFirst(); + while (entry) { + x0 = entry.pos - entry.size; + x1 = entry.pos; if (x0 <= start && start < x1) { // when a whole row (column) is selected the cell cursor is moved to the first column (row) // but this action should not cause to select/unselect anything, on the contrary we end up // with all column (row) header entries selected but the one where the cell cursor was // previously placed if (this._selection.start === -1 && this._selection.end === -1) { - this.unselect(data, this._current); - this.select(data, iterator); + this.unselect(data.get(this._current)); + this.select(entry); } - this._current = iterator; + this._current = entry.index; break; } + entry = data.getNext(); } }, @@ -197,10 +224,10 @@ L.Control.Header = L.Control.extend({ }, _onMouseOut: function (e) { - if (this._mouseOverIndex) { - this.drawHeaderEntry(this._mouseOverIndex, false); - this._lastMouseOverIndex = this._mouseOverIndex; // used by context menu - this._mouseOverIndex = undefined; + if (this._mouseOverEntry) { + this.drawHeaderEntry(this._mouseOverEntry, false); + this._lastMouseOverIndex = this._mouseOverEntry.index; // used by context menu + this._mouseOverEntry = null; } this._hitResizeArea = false; L.DomUtil.setStyle(this._headerCanvas, 'cursor', this._cursor); @@ -217,25 +244,24 @@ L.Control.Header = L.Control.extend({ var pos = this._getPos(this._mouseEventToCanvasPos(this._headerCanvas, e)); pos = pos - this._position; - var mouseOverIndex = this._mouseOverIndex; - for (var iterator = 1; iterator < this._data.length; ++iterator) { - var start = this._data[iterator - 1].pos; - var end = this._data[iterator].pos; + var mouseOverIndex = this._mouseOverEntry ? this._mouseOverEntry.index : undefined; + var entry = this._data.getFirst(); + while (entry) { + var start = entry.pos - entry.size; + var end = entry.pos; if (pos > start && pos <= end) { - mouseOverIndex = iterator; + mouseOverIndex = entry.index; var resizeAreaStart = Math.max(start, end - 3); isMouseOverResizeArea = (pos > resizeAreaStart); break; } + entry = this._data.getNext(); } - if (mouseOverIndex !== this._mouseOverIndex) { - if (this._mouseOverIndex) { - this.drawHeaderEntry(this._mouseOverIndex, false); - } - if (mouseOverIndex) { - this.drawHeaderEntry(mouseOverIndex, true); - } + if (mouseOverIndex && (!this._mouseOverEntry || mouseOverIndex !== this._mouseOverEntry.index)) { + this.drawHeaderEntry(this._mouseOverEntry, false); + this.drawHeaderEntry(entry, true); + this._mouseOverEntry = entry; } if (isMouseOverResizeArea !== this._hitResizeArea) { @@ -249,8 +275,6 @@ L.Control.Header = L.Control.extend({ L.DomUtil.setStyle(this._headerCanvas, 'cursor', cursor); this._hitResizeArea = isMouseOverResizeArea; } - - this._mouseOverIndex = mouseOverIndex; }, _onMouseDown: function (e) { @@ -311,6 +335,13 @@ L.Control.Header = L.Control.extend({ this._dragging = false; }, + _twipsToPixels: function (twips) { + if (!this.converter) + return 0; + var point = new L.Point(twips, twips); + return Math.round(this._getPos(this.converter(point))); + }, + onDragStart: function () {}, onDragMove: function () {}, onDragEnd: function () {}, @@ -319,3 +350,154 @@ L.Control.Header = L.Control.extend({ drawHeaderEntry: function () {}, _getPos: function () {} }); + +(function () { + + L.Control.Header.DataImpl = L.Class.extend({ + initialize: function () { + this.converter = null; + + this._currentIndex = undefined; + this._currentRange = undefined; + this._dataMap = {}; + this._indexes = []; + this._endIndex = -1; + this._skipZeroSize = true; + }, + + _get: function (index, setCurrentIndex) { + if (index < 1 || index > this._endIndex) + return null; + + var range = this._getFirstIndexLessOrEqual(index); + if (range !== undefined) { + if (setCurrentIndex) { + this._currentRange = range; + this._currentIndex = index; + } + return this._computeEntry(this._indexes[range], index); + } + }, + + get: function (index) { + return this._get(index, false); + }, + + getAt: function (index) { + return this._get(index, true); + }, + + getFirst: function () { + this._currentRange = 0; + this._currentIndex = this._indexes[this._currentRange]; + return this.getNext(); + }, + + getNext: function () { + if (this._currentIndex === undefined || this._currentRange === undefined) + return null; // you need to call getFirst on initial step + + this._currentIndex += 1; + if (this._currentIndex >= this._endIndex) { + // we iterated over all entries, reset everything + this._currentIndex = undefined; + this._currentRange = undefined; + this._skipZeroSize = false; + return null; + } + + if (this._indexes[this._currentRange+1] === this._currentIndex) { + // new range + this._currentRange += 1; + + if (this._skipZeroSize) { + var index, i, len = this._indexes.length; + for (i = this._currentRange; i < len; ++i) { + index = this._indexes[i]; + if (this._dataMap[index].size > 0) { + break; + } + } + this._currentRange = i; + this._currentIndex = index; + } + } + + var startIndex = this._indexes[this._currentRange]; + return this._computeEntry(startIndex, this._currentIndex); + }, + + pushBack: function (index, value) { + if (index <= this._endIndex) + return; + this._dataMap[index] = value; + this._indexes.push(index); + this._endIndex = index; + }, + + isZeroSize: function (index) { + if (!(index > 0 && index < this._endIndex)) { + return true; + } + + var range = this._getFirstIndexLessOrEqual(index); + return this._dataMap[this._indexes[range]].size === 0; + }, + + getLength: function () { + return this._endIndex; + }, + + isEmpty: function () { + return this._indexes.length === 0; + }, + + _binaryIndexOf: function (collection, searchElement) { + var minIndex = 0; + var maxIndex = collection.length - 1; + var currentIndex; + var currentElement; + + while (minIndex <= maxIndex) { + currentIndex = (minIndex + maxIndex) / 2 | 0; + currentElement = collection[currentIndex]; + + if (currentElement < searchElement) { + minIndex = currentIndex + 1; + } + else if (currentElement > searchElement) { + maxIndex = currentIndex - 1; + } + else { + return currentIndex; + } + } + + if (currentIndex > maxIndex) + return currentIndex - 1; + if (currentIndex < minIndex) + return currentIndex; + }, + + _getFirstIndexLessOrEqual: function (index) { + return this._binaryIndexOf(this._indexes, index); + }, + + _twipsToPixels: function (twips) { + if (!this.converter) + return 0; + + return this.converter(twips); + }, + + _computeEntry: function (startIndex, index) { + var entry = this._dataMap[startIndex]; + var pos = entry.pos + (index - startIndex) * entry.size; + pos = this._twipsToPixels(pos); + var size = this._twipsToPixels(entry.size); + return {index: index, pos: pos, size: size}; + } + + }); + +})(); \ No newline at end of file diff --git a/loleaflet/src/control/Control.RowHeader.js b/loleaflet/src/control/Control.RowHeader.js index 520a0893..4763f1b1 100644 --- a/loleaflet/src/control/Control.RowHeader.js +++ b/loleaflet/src/control/Control.RowHeader.js @@ -55,8 +55,7 @@ L.Control.RowHeader = L.Control.Header.extend({ callback: function(key, options) { var index = rowHeaderObj._lastMouseOverIndex; if (index) { - var row = rowHeaderObj._data[index].text; - rowHeaderObj.insertRow.call(rowHeaderObj, row); + rowHeaderObj.insertRow.call(rowHeaderObj, index); } } }, @@ -65,8 +64,7 @@ L.Control.RowHeader = L.Control.Header.extend({ callback: function(key, options) { var index = rowHeaderObj._lastMouseOverIndex; if (index) { - var row = rowHeaderObj._data[index].text; - rowHeaderObj.deleteRow.call(rowHeaderObj, row); + rowHeaderObj.deleteRow.call(rowHeaderObj, index); } } }, @@ -75,8 +73,7 @@ L.Control.RowHeader = L.Control.Header.extend({ callback: function(key, options) { var index = rowHeaderObj._lastMouseOverIndex; if (index) { - var row = rowHeaderObj._data[index].text; - rowHeaderObj.optimalHeight.call(rowHeaderObj, row); + rowHeaderObj.optimalHeight.call(rowHeaderObj, index); } } }, @@ -85,8 +82,7 @@ L.Control.RowHeader = L.Control.Header.extend({ callback: function(key, options) { var index = rowHeaderObj._lastMouseOverIndex; if (index) { - var row = rowHeaderObj._data[index].text; - rowHeaderObj.hideRow.call(rowHeaderObj, row); + rowHeaderObj.hideRow.call(rowHeaderObj, index); } } }, @@ -95,8 +91,7 @@ L.Control.RowHeader = L.Control.Header.extend({ callback: function(key, options) { var index = rowHeaderObj._lastMouseOverIndex; if (index) { - var row = rowHeaderObj._data[index].text; - rowHeaderObj.showRow.call(rowHeaderObj, row); + rowHeaderObj.showRow.call(rowHeaderObj, index); } } } @@ -105,45 +100,45 @@ L.Control.RowHeader = L.Control.Header.extend({ }); }, - optimalHeight: function(row) { + optimalHeight: function(index) { if (!this._dialog) { this._dialog = L.control.metricInput(this._onDialogResult, this, 0, {title: _('Optimal Row Height')}); } if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectRow(row, 0); + this._selectRow(index, 0); } this._dialog.addTo(this._map); this._map.enable(false); this._dialog.show(); }, - insertRow: function(row) { + insertRow: function(index) { // First select the corresponding row because // .uno:InsertRows doesn't accept any row number // as argument and just inserts before the selected row if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectRow(row, 0); + this._selectRow(index, 0); } this._map.sendUnoCommand('.uno:InsertRows'); }, - deleteRow: function(row) { + deleteRow: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectRow(row, 0); + this._selectRow(index, 0); } this._map.sendUnoCommand('.uno:DeleteRows'); }, - hideRow: function(row) { + hideRow: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectRow(row, 0); + this._selectRow(index, 0); } this._map.sendUnoCommand('.uno:HideRow'); }, - showRow: function(row) { + showRow: function(index) { if (this._map._docLayer._selections.getLayers().length === 0) { - this._selectRow(row, 0); + this._selectRow(index, 0); } this._map.sendUnoCommand('.uno:ShowRow'); }, @@ -163,50 +158,43 @@ L.Control.RowHeader = L.Control.Header.extend({ }, _onUpdateSelection: function (e) { - var data = this._data; - if (!data) - return; var start = e.start.y; var end = e.end.y; - var twips; if (start !== -1) { - twips = new L.Point(start, start); - start = Math.round(data.converter.call(data.context, twips).y); + start = this._twipsToPixels(start); } if (end !== -1) { - twips = new L.Point(end, end); - end = Math.round(data.converter.call(data.context, twips).y); + end = this._twipsToPixels(end); } - this.updateSelection(data, start, end); + this.updateSelection(this._data, start, end); }, _onUpdateCurrentRow: function (e) { - var data = this._data; - if (!data) - return; var y = e.y; if (y !== -1) { - var twips = new L.Point(y, y); - y = Math.round(data.converter.call(data.context, twips).y); + y = this._twipsToPixels(y); } - this.updateCurrent(data, y); + this.updateCurrent(this._data, y); }, _updateRowHeader: function () { this._map.fire('updaterowcolumnheaders', {x: 0, y: this._map._getTopLeftPoint().y, offset: {x: 0, y: undefined}}); }, - drawHeaderEntry: function (index, isOver) { - if (!index || index <= 0 || index >= this._data.length) + drawHeaderEntry: function (entry, isOver, isHighlighted) { + if (!entry) return; var ctx = this._canvasContext; - var content = this._data[index].text; - var start = this._data[index - 1].pos - this._topOffset; - var end = this._data[index].pos - this._topOffset; + var content = entry.index + this._topRow; + var start = entry.pos - entry.size - this._topOffset; + var end = entry.pos - this._topOffset; var height = end - start; var width = this._headerCanvas.width; - var isHighlighted = this._data[index].selected; + + if (isHighlighted !== true && isHighlighted !== false) { + isHighlighted = this.isHighlighted(entry.index); + } if (height <= 0) return; @@ -241,22 +229,23 @@ L.Control.RowHeader = L.Control.Header.extend({ }, getHeaderEntryBoundingClientRect: function (index) { - if (!index) - index = this._mouseOverIndex; // use last mouse over position + var entry = this._mouseOverEntry; + if (index) + entry = this._data.get(index); - if (!index || !this._data[index]) + if (!entry) return; var rect = this._headerCanvas.getBoundingClientRect(); - var rowStart = this._data[index - 1].pos + this._position; - var rowEnd = this._data[index].pos + this._position; + var rowStart = entry.pos - entry.size + this._position; + var rowEnd = entry.pos + this._position; var left = rect.left; var right = rect.right; var top = rect.top + rowStart; var bottom = rect.top + rowEnd; - return { left: left, right: right, top: top, bottom: bottom }; + return {left: left, right: right, top: top, bottom: bottom}; }, viewRowColumnHeaders: function (e) { @@ -266,31 +255,60 @@ L.Control.RowHeader = L.Control.Header.extend({ }, fillRows: function (rows, converter, context) { - var iterator, twip, height; + if (rows.length < 2) + return; - this._data = new Array(rows.length); - this._data.converter = converter; - this._data.context = context; + var entry, index, iterator, height, pos; var canvas = this._headerCanvas; canvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width')); canvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height')); - this._canvasContext.clearRect(0, 0, canvas.width, canvas.height); - var topOffset = new L.Point(rows[0].size, rows[0].size); + // update first header index and reset no more valid variables this._topRow = parseInt(rows[0].text); - this._topOffset = Math.round(converter.call(context, topOffset).y); - - this._data[0] = { pos: this._topOffset, text: '', selected: false }; + this._current = -1; + this._selection.start = this._selection.end = -1; + this._mouseOverEntry = null; + this._lastMouseOverIndex = undefined; + + // create header data handler instance + this._data = new L.Control.Header.DataImpl(); + + // setup conversion routine + this.converter = L.Util.bind(converter, context); + this._data.converter = L.Util.bind(this._twipsToPixels, this); + + var startOffsetTw = parseInt(rows[0].size); + this._topOffset = this._twipsToPixels(startOffsetTw); + + this._data.pushBack(0, {pos: startOffsetTw, size: 0}); + var prevPos = startOffsetTw; + var nextIndex = parseInt(rows[1].text); + var last = rows.length - 1; + + for (iterator = 1; iterator < last; iterator++) { + index = nextIndex; + pos = parseInt(rows[iterator].size); + nextIndex = parseInt(rows[iterator+1].text); + height = pos - prevPos; + prevPos = Math.round(pos + height * (nextIndex - index - 1)); + index = index - this._topRow; + entry = {pos: pos, size: height}; + this._data.pushBack(index, entry); + } - for (iterator = 1; iterator < rows.length; iterator++) { - twip = new L.Point(rows[iterator].size, rows[iterator].size); - this._data[iterator] = { pos: Math.round(converter.call(context, twip).y), text: rows[iterator].text, selected: false }; - height = this._data[iterator].pos - this._data[iterator - 1].pos; - if (height > 0) { - this.drawHeaderEntry(iterator, false); - } + // setup last header entry + index = nextIndex - this._topRow; + pos = parseInt(rows[last].size); + height = pos - prevPos; + this._data.pushBack(index, {pos: pos, size: height}); + + // draw header + entry = this._data.getFirst(); + while (entry) { + this.drawHeaderEntry(entry, false); + entry = this._data.getNext(); } this.mouseInit(canvas); @@ -305,7 +323,7 @@ L.Control.RowHeader = L.Control.Header.extend({ var command = { Row: { type: 'long', - value: parseInt(row - 1) + value: row - 1 }, Modifier: { type: 'unsigned short', @@ -317,10 +335,10 @@ L.Control.RowHeader = L.Control.Header.extend({ }, _onHeaderClick: function (e) { - if (!this._mouseOverIndex) + if (!this._mouseOverEntry) return; - var row = this._mouseOverIndex + this._topRow; + var row = this._mouseOverEntry.index + this._topRow; var modifier = 0; if (e.shiftKey) { @@ -378,14 +396,14 @@ L.Control.RowHeader = L.Control.Header.extend({ var end = new L.Point(e.clientX, e.clientY + offset.y); var distance = this._map._docLayer._pixelsToTwips(end.subtract(start)); - if (this._mouseOverIndex) { - var clickedRow = this._data[this._mouseOverIndex]; - var height = clickedRow.pos - this._data[this._mouseOverIndex - 1]; - var row = this._mouseOverIndex + this._topRow; + var clickedRow = this._mouseOverEntry; + if (clickedRow) { + var height = clickedRow.size; + var row = clickedRow.index + this._topRow; - if (this._data[this._mouseOverIndex + 1] - && this._data[this._mouseOverIndex + 1].pos === clickedRow.pos) { + if (this._data.isZeroSize(clickedRow.index + 1)) { row += 1; + height = 0; } if (height !== distance.y) { @@ -410,11 +428,11 @@ L.Control.RowHeader = L.Control.Header.extend({ onDragClick: function (item, clicks, e) { this._map.removeLayer(this._horzLine); - if (!this._mouseOverIndex) + if (!this._mouseOverEntry) return; if (clicks === 2) { - var row = this._mouseOverIndex + this._topRow; + var row = this._mouseOverEntry.index + this._topRow; var command = { Row: { type: 'long', _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits