loleaflet/dist/spreadsheet.css | 27 +- loleaflet/src/control/Control.ColumnHeader.js | 265 +++++++++++++++---- loleaflet/src/control/Control.Header.js | 347 ++++++++++++++++++++++++-- loleaflet/src/control/Control.Menubar.js | 9 loleaflet/src/control/Control.RowHeader.js | 248 +++++++++++++++--- loleaflet/src/control/Control.Scroll.js | 5 loleaflet/src/dom/DomUtil.js | 12 loleaflet/src/layer/tile/CalcTileLayer.js | 4 8 files changed, 788 insertions(+), 129 deletions(-)
New commits: commit 6311305822aec8d1e94212a1a98f188b850be0eb Author: Marco Cecchetti <marco.cecche...@collabora.com> Date: Mon Nov 27 19:37:53 2017 +0100 calc: outline and groups handling Change-Id: Ie7dcb9a742344e6b0a8813faebc589167a457261 Reviewed-on: https://gerrit.libreoffice.org/45418 Reviewed-by: Jan Holesovsky <ke...@collabora.com> Tested-by: Jan Holesovsky <ke...@collabora.com> diff --git a/loleaflet/dist/spreadsheet.css b/loleaflet/dist/spreadsheet.css index 8435b271..e97f6041 100644 --- a/loleaflet/dist/spreadsheet.css +++ b/loleaflet/dist/spreadsheet.css @@ -59,7 +59,7 @@ } #spreadsheet-row-column-frame { - position: absolute; + position: absolute; left: 0; right: 0; top: 103px; @@ -70,7 +70,7 @@ top: 30px; } -.spreadsheet-header-corner { +#spreadsheet-header-corner-container { border: 1px solid darkgrey; background-color: lightgrey; cursor: pointer; @@ -84,7 +84,26 @@ height: 19px; } -.spreadsheet-header-columns-container { +#spreadsheet-header-corner { + display: inline-block; + white-space: nowrap; + width: 100%; + height: 100%; + border-spacing: 0px !important; + position: relative; + margin: 0px; + padding: 0px; + } + +.spreadsheet-header-corner-styles { + border: 1px solid darkgray; + font: 12px/1.5 "Segoe UI", Tahoma, Arial, Helvetica, sans-serif; + color: black; + background-color: lightgray; + cursor: pointer; + } + +#spreadsheet-header-columns-container { border: 1px solid darkgrey; background-color: lightgrey; @@ -130,7 +149,7 @@ cursor: col-resize; } -.spreadsheet-header-rows-container { +#spreadsheet-header-rows-container { border: 1px solid darkgrey; background-color: lightgrey; diff --git a/loleaflet/src/control/Control.ColumnHeader.js b/loleaflet/src/control/Control.ColumnHeader.js index 264fea4d..ae95f183 100644 --- a/loleaflet/src/control/Control.ColumnHeader.js +++ b/loleaflet/src/control/Control.ColumnHeader.js @@ -15,38 +15,44 @@ L.Control.ColumnHeader = L.Control.Header.extend({ _initialize: function () { this._initialized = true; + this._isColumn = true; this._map.on('scrolloffset', this.offsetScrollPosition, this); this._map.on('updatescrolloffset', this.setScrollPosition, this); this._map.on('viewrowcolumnheaders', this.viewRowColumnHeaders, this); this._map.on('updateselectionheader', this._onUpdateSelection, this); this._map.on('clearselectionheader', this._onClearSelection, this); this._map.on('updatecurrentheader', this._onUpdateCurrentColumn, this); + this._map.on('updatecornerheader', this.drawCornerHeader, this); var rowColumnFrame = L.DomUtil.get('spreadsheet-row-column-frame'); - var cornerHeader = L.DomUtil.create('div', 'spreadsheet-header-corner', rowColumnFrame); - L.DomEvent.on(cornerHeader, 'contextmenu', L.DomEvent.preventDefault); - L.DomEvent.addListener(cornerHeader, 'click', this._onCornerHeaderClick, this); - this._headersContainer = L.DomUtil.create('div', 'spreadsheet-header-columns-container', rowColumnFrame); + this._headerContainer = L.DomUtil.createWithId('div', 'spreadsheet-header-columns-container', rowColumnFrame); this._initHeaderEntryStyles('spreadsheet-header-column'); this._initHeaderEntryHoverStyles('spreadsheet-header-column-hover'); this._initHeaderEntrySelectedStyles('spreadsheet-header-column-selected'); this._initHeaderEntryResizeStyles('spreadsheet-header-column-resize'); - this._headerCanvas = L.DomUtil.create('canvas', 'spreadsheet-header-columns', this._headersContainer); - this._canvasContext = this._headerCanvas.getContext('2d'); - this._headerCanvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width')); - this._headerCanvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height')); + this._canvas = L.DomUtil.create('canvas', 'spreadsheet-header-columns', this._headerContainer); + this._canvasContext = this._canvas.getContext('2d'); + this._setCanvasWidth(); + this._setCanvasHeight(); + this._headerHeight = this._canvas.height; + L.Control.Header.colHeaderHeight = this._canvas.height; - L.DomUtil.setStyle(this._headerCanvas, 'cursor', this._cursor); + L.DomUtil.setStyle(this._canvas, 'cursor', this._cursor); - L.DomEvent.on(this._headerCanvas, 'mousemove', this._onCanvasMouseMove, this); - L.DomEvent.on(this._headerCanvas, 'mouseout', this._onMouseOut, this); - L.DomEvent.on(this._headerCanvas, 'click', this._onHeaderClick, this); + L.DomEvent.on(this._canvas, 'mousemove', this._onMouseMove, this); + L.DomEvent.on(this._canvas, 'mouseout', this._onMouseOut, this); + L.DomEvent.on(this._canvas, 'click', this._onClick, this); + L.DomEvent.on(this._canvas, 'dblclick', this._onDoubleClick, this); - this._leftmostColumn = 0; - this._leftOffset = 0; + this._startHeaderIndex = 0; + this._startOffset = 0; this._position = 0; + L.DomEvent.on(this._cornerCanvas, 'contextmenu', L.DomEvent.preventDefault); + L.DomEvent.addListener(this._cornerCanvas, 'click', this._onCornerHeaderClick, this); + + var colHeaderObj = this; $.contextMenu({ selector: '.spreadsheet-header-columns', @@ -178,11 +184,13 @@ L.Control.ColumnHeader = L.Control.Header.extend({ }, _onUpdateCurrentColumn: function (e) { - var x = e.x; + var x = e.min.x; + var w = e.getSize().x; if (x !== -1) { x = this._twipsToPixels(x); + w = this._twipsToPixels(w); } - this.updateCurrent(this._data, x); + this.updateCurrent(this._data, x, w); }, _updateColumnHeader: function () { @@ -194,46 +202,133 @@ L.Control.ColumnHeader = L.Control.Header.extend({ return; var ctx = this._canvasContext; - 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 content = this._colIndexToAlpha(entry.index + this._startHeaderIndex); + var startOrt = this._canvas.height - this._headerHeight; + var startPar = entry.pos - entry.size - this._startOffset; + var endPar = entry.pos - this._startOffset; + var width = endPar - startPar; + var height = this._headerHeight; if (isHighlighted !== true && isHighlighted !== false) { isHighlighted = this.isHighlighted(entry.index); } - if (width <= 0) return; ctx.save(); - ctx.translate(this._position + this._leftOffset, 0); + ctx.translate(this._position + this._startOffset, 0); // background gradient var selectionBackgroundGradient = null; if (isHighlighted) { - selectionBackgroundGradient = ctx.createLinearGradient(start, 0, start, height); + selectionBackgroundGradient = ctx.createLinearGradient(startPar, startOrt, startPar, startOrt + height); selectionBackgroundGradient.addColorStop(0, this._selectionBackgroundGradient[0]); selectionBackgroundGradient.addColorStop(0.5, this._selectionBackgroundGradient[1]); selectionBackgroundGradient.addColorStop(1, this._selectionBackgroundGradient[2]); } + + // draw header/outline border separator + if (this._headerHeight !== this._canvas.height) { + ctx.fillStyle = this._borderColor; + ctx.fillRect(startPar, startOrt - this._borderWidth, width, this._borderWidth); + } + // clip mask ctx.beginPath(); - ctx.rect(start, 0, width, height); + ctx.rect(startPar, startOrt, width, height); ctx.clip(); // draw background ctx.fillStyle = isHighlighted ? selectionBackgroundGradient : isOver ? this._hoverColor : this._backgroundColor; - ctx.fillRect(start, 0, width, height); + ctx.fillRect(startPar, startOrt, width, height); // draw text content ctx.fillStyle = isHighlighted ? this._selectionTextColor : this._textColor; ctx.font = this._font; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; - ctx.fillText(content, end - width / 2, height / 2); + ctx.fillText(content, endPar - (width / 2), startOrt + (height / 2)); // draw row separator ctx.fillStyle = this._borderColor; - ctx.fillRect(end -1, 0, this._borderWidth, height); + ctx.fillRect(endPar -1, startOrt, this._borderWidth, height); + ctx.restore(); + }, + + drawGroupControl: function (group) { + if (!group) + return; + + var ctx = this._canvasContext; + var headSize = this._groupHeadSize; + var spacing = this._levelSpacing; + var level = group.level; + + var startOrt = spacing + (headSize + spacing) * level; + var startPar = group.startPos - this._startOffset; + var height = group.endPos - group.startPos; + + ctx.save(); + ctx.translate(this._position + this._startOffset, 0); + // clip mask + ctx.beginPath(); + ctx.rect(startPar, startOrt, height, headSize); + ctx.clip(); + if (!group.hidden) { + //draw tail + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1.5; + ctx.beginPath(); + ctx.moveTo(startPar + headSize, startOrt + 2); + ctx.lineTo(startPar + height - 1, startOrt + 2); + ctx.lineTo(startPar + height - 1, startOrt + 2 + headSize / 2); + ctx.stroke(); + // draw head + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startPar, startOrt, headSize, headSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startPar, startOrt, headSize, headSize); + // draw '-' + ctx.lineWidth = 1; + ctx.strokeRect(startPar + headSize / 4, startOrt + headSize / 2, headSize / 2, 1); + } + else { + // draw head + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startPar, startOrt, headSize, headSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startPar, startOrt, headSize, headSize); + // draw '+' + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(startPar + headSize / 4, startOrt + headSize / 2); + ctx.lineTo(startPar + 3 * headSize / 4, startOrt + headSize / 2); + ctx.moveTo(startPar + headSize / 2, startOrt + headSize / 4); + ctx.lineTo(startPar + headSize / 2, startOrt + 3 * headSize / 4); + ctx.stroke(); + } + ctx.restore(); + }, + + drawLevelHeader: function(level) { + var ctx = this._cornerCanvasContext; + var ctrlHeadSize = this._groupHeadSize; + var levelSpacing = this._levelSpacing; + + var startOrt = levelSpacing + (ctrlHeadSize + levelSpacing) * level; + var startPar = this._cornerCanvas.width - (ctrlHeadSize + (L.Control.Header.rowHeaderWidth - ctrlHeadSize) / 2); + + ctx.save(); + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startPar, startOrt, ctrlHeadSize, ctrlHeadSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startPar, startOrt, ctrlHeadSize, ctrlHeadSize); + // draw level number + ctx.fillStyle = this._textColor; + ctx.font = this._font; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(level + 1, startPar + (ctrlHeadSize / 2), startOrt + (ctrlHeadSize / 2)); ctx.restore(); }, @@ -245,7 +340,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ if (!entry) return; - var rect = this._headerCanvas.getBoundingClientRect(); + var rect = this._canvas.getBoundingClientRect(); var colStart = entry.pos - entry.size + this._position; var colEnd = entry.pos + this._position; @@ -259,23 +354,23 @@ L.Control.ColumnHeader = L.Control.Header.extend({ viewRowColumnHeaders: function (e) { if (e.data.columns && e.data.columns.length > 0) { - this.fillColumns(e.data.columns, e.converter, e.context); + this.fillColumns(e.data.columns, e.data.columnGroups, e.converter, e.context); } }, - fillColumns: function (columns, converter, context) { + fillColumns: function (columns, colGroups, converter, context) { if (columns.length < 2) return; - var entry, index, iterator, pos, width; + var headerEntry, index, iterator, width, pos; - var canvas = this._headerCanvas; - canvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width')); - canvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height')); + var canvas = this._canvas; + this._setCanvasWidth(); + this._setCanvasHeight(); this._canvasContext.clearRect(0, 0, canvas.width, canvas.height); // update first header index and reset no more valid variables - this._leftmostColumn = parseInt(columns[0].text); + this._startHeaderIndex = parseInt(columns[0].text); this._current = -1; // no more valid this._selection.start = this._selection.end = -1; // no more valid this._mouseOverEntry = null; @@ -288,35 +383,57 @@ L.Control.ColumnHeader = L.Control.Header.extend({ this.converter = L.Util.bind(converter, context); this._data.converter = L.Util.bind(this._twipsToPixels, this); + // create group array + this._groupLevels = parseInt(columns[0].groupLevels); + this._groups = this._groupLevels ? new Array(this._groupLevels) : null; + var startOffsetTw = parseInt(columns[0].size); - this._leftOffset = this._twipsToPixels(startOffsetTw); + this._startOffset = 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); + index = index - this._startHeaderIndex; + headerEntry = {pos: pos, size: width}; + this._data.pushBack(index, headerEntry); } - // setup last header entry + // setup last header headerEntry + index = nextIndex - this._startHeaderIndex; pos = parseInt(columns[last].size); - this._data.pushBack(nextIndex - this._leftmostColumn, {pos: pos, size: pos - prevPos}); + width = pos - prevPos; + this._data.pushBack(index, {pos: pos, size: pos - width}); + + // collect group controls data + if (colGroups !== undefined && this._groups) { + this._collectGroupsData(colGroups); + } + + if (this._groups) { + this.resize(this._computeOutlineWidth() + this._borderWidth + this._headerHeight); + } + else if (this._canvas.height !== this._headerHeight) { + this.resize(this._headerHeight); + } // draw header - entry = this._data.getFirst(); - while (entry) { - this.drawHeaderEntry(entry, false); - entry = this._data.getNext(); + headerEntry = this._data.getFirst(); + while (headerEntry) { + this.drawHeaderEntry(headerEntry, false); + headerEntry = this._data.getNext(); } + // draw group controls + this.drawOutline(); + this.mouseInit(canvas); L.DomEvent.on(canvas, 'contextmenu', L.DomEvent.preventDefault); @@ -366,11 +483,14 @@ L.Control.ColumnHeader = L.Control.Header.extend({ this._map.sendUnoCommand('.uno:SelectColumn ', command); }, - _onHeaderClick: function (e) { + _onClick: function (e) { + if (this._onOutlineMouseEvent(e, this._onGroupControlClick)) + return; + if (!this._mouseOverEntry) return; - var col = this._mouseOverEntry.index + this._leftmostColumn; + var col = this._mouseOverEntry.index + this._startHeaderIndex; var modifier = 0; if (e.shiftKey) { @@ -383,8 +503,22 @@ L.Control.ColumnHeader = L.Control.Header.extend({ this._selectColumn(col, modifier); }, - _onCornerHeaderClick: function() { - this._map.sendUnoCommand('.uno:SelectAll'); + _onCornerHeaderClick: function(e) { + var pos = this._mouseEventToCanvasPos(this._cornerCanvas, e); + + if (pos.y > this.getOutlineWidth()) { + this._map.fire('cornerheaderclicked', e); + return; + } + + var rowOutlineWidth = this._cornerCanvas.width - L.Control.Header.rowHeaderWidth - this._borderWidth; + if (pos.x <= rowOutlineWidth) { + // empty rectangle on the left select all + this._map.sendUnoCommand('.uno:SelectAll'); + } + + var level = this._getGroupLevel(pos.y); + this._updateOutlineState(/*is column: */ true, {column: true, level: level, index: -1}); }, _onDialogResult: function (e) { @@ -435,7 +569,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ var clickedColumn = this._mouseOverEntry; if (clickedColumn) { var width = clickedColumn.size; - var column = clickedColumn.index + this._leftmostColumn; + var column = clickedColumn.index + this._startHeaderIndex; if (this._data.isZeroSize(clickedColumn.index + 1)) { column += 1; @@ -469,7 +603,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({ return; if (clicks === 2) { - var column = this._mouseOverEntry.index + this._leftmostColumn; + var column = this._mouseOverEntry.index + this._startHeaderIndex; var command = { Col: { type: 'unsigned short', @@ -499,9 +633,34 @@ L.Control.ColumnHeader = L.Control.Header.extend({ } }, - _getPos: function (point) { + _getParallelPos: function (point) { return point.x; + }, + + _getOrthogonalPos: function (point) { + return point.y; + }, + + resize: function (height) { + if (height < this._headerHeight) + return; + + var rowHeader = L.DomUtil.get('spreadsheet-header-rows-container'); + var document = L.DomUtil.get('document-container'); + + this._setCornerCanvasHeight(height); + var deltaTop = height - this._canvas.height; + var rowHdrTop = parseInt(L.DomUtil.getStyle(rowHeader, 'top')) + deltaTop; + var docTop = parseInt(L.DomUtil.getStyle(document, 'top')) + deltaTop; + console.log('resize: height: ' + height + ', deltaTop: ' + deltaTop + ', rowHdrTop: ' + rowHdrTop + ', docTop: ' + docTop); + L.DomUtil.setStyle(rowHeader, 'top', rowHdrTop + 'px'); + L.DomUtil.setStyle(document, 'top', docTop + 'px'); + + this._setCanvasHeight(height); + + this._map.fire('updatecornerheader'); } + }); L.control.columnHeader = function (options) { diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js index d7689ee0..6dac7330 100644 --- a/loleaflet/src/control/Control.Header.js +++ b/loleaflet/src/control/Control.Header.js @@ -8,17 +8,42 @@ L.Control.Header = L.Control.extend({ }, initialize: function () { + this._isColumn = undefined; + this.converter = null; - this._headerCanvas = null; + this._canvas = null; this._clicks = 0; this._current = -1; this._selection = {start: -1, end: -1}; this._mouseOverEntry = null; this._lastMouseOverIndex = undefined; this._hitResizeArea = false; + this._overHeaderArea = false; this._selectionBackgroundGradient = [ '#3465A4', '#729FCF', '#004586' ]; + + this._groups = null; + + // group control styles + this._groupHeadSize = 12; + this._levelSpacing = 1; + + // set up corner header + var cornerHeader = L.DomUtil.get('spreadsheet-header-corner-container'); + if (cornerHeader) { + this._cornerHeaderContainer = cornerHeader; + this._cornerCanvas = L.DomUtil.get('spreadsheet-header-corner'); + } + else { + var rowColumnFrame = L.DomUtil.get('spreadsheet-row-column-frame'); + this._cornerHeaderContainer = L.DomUtil.createWithId('div', 'spreadsheet-header-corner-container', rowColumnFrame); + this._cornerCanvas = L.DomUtil.createWithId('canvas', 'spreadsheet-header-corner', this._cornerHeaderContainer); + this._setCornerCanvasWidth(); + this._setCornerCanvasHeight(); + } + this._cornerCanvasContext = this._cornerCanvas.getContext('2d'); + this._cornerCanvasContext.clearRect(0, 0, this._cornerCanvas.width, this._cornerCanvas.height); }, _initHeaderEntryStyles: function (className) { @@ -184,7 +209,7 @@ L.Control.Header = L.Control.extend({ this._selection.end = itEnd; }, - updateCurrent: function (data, start) { + updateCurrent: function (data, start, size) { if (!data || data.isEmpty()) return; @@ -195,22 +220,29 @@ L.Control.Header = L.Control.extend({ } var x0 = 0, x1 = 0; + var prevEntry = null; var entry = data.getFirst(); + var zeroSizeEntry = false; while (entry) { x0 = entry.pos - entry.size; x1 = entry.pos; if (x0 <= start && start < x1) { + // we have a slim cursor because of a zero size entry ? + zeroSizeEntry = size <= 1 && prevEntry && prevEntry.size === 0; // 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.get(this._current)); - this.select(entry); + // no selection when the cell cursor is slim + if (!zeroSizeEntry) + this.select(entry); } - this._current = entry.index; + this._current = zeroSizeEntry ? -1 : entry.index; break; } + prevEntry = entry; entry = data.getNext(); } }, @@ -224,24 +256,39 @@ L.Control.Header = L.Control.extend({ }, _onMouseOut: function (e) { + if (this._hitOutline(e)) + return; + + this._onHeaderMouseOut(e); + }, + + _onHeaderMouseOut: function (e) { + if (!this._overHeaderArea) + return; + this._overHeaderArea = false; + if (this._mouseOverEntry) { - this.drawHeaderEntry(this._mouseOverEntry, false); + this.drawHeaderEntry(this._mouseOverEntry, /*isOver: */ false); this._lastMouseOverIndex = this._mouseOverEntry.index; // used by context menu this._mouseOverEntry = null; } this._hitResizeArea = false; - L.DomUtil.setStyle(this._headerCanvas, 'cursor', this._cursor); + L.DomEvent.on(this._canvas, 'click', this._onClick, this); + L.DomUtil.setStyle(this._canvas, 'cursor', 'default'); }, - _onCanvasMouseMove: function (e) { - var target = e.target || e.srcElement; - - if (!target || this._dragging) { + _onMouseMove: function (e) { + if (this._hitOutline(e)) { + this._onHeaderMouseOut(e); return false; } + if (!this._overHeaderArea) { + L.DomUtil.setStyle(this._canvas, 'cursor', this._cursor); + this._overHeaderArea = true; + } var isMouseOverResizeArea = false; - var pos = this._getPos(this._mouseEventToCanvasPos(this._headerCanvas, e)); + var pos = this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e)); pos = pos - this._position; var mouseOverIndex = this._mouseOverEntry ? this._mouseOverEntry.index : undefined; @@ -266,20 +313,91 @@ L.Control.Header = L.Control.extend({ if (isMouseOverResizeArea !== this._hitResizeArea) { if (isMouseOverResizeArea) { - L.DomEvent.off(this._headerCanvas, 'click', this._onHeaderClick, this); + L.DomEvent.off(this._canvas, 'click', this._onClick, this); } else { - L.DomEvent.on(this._headerCanvas, 'click', this._onHeaderClick, this); + L.DomEvent.on(this._canvas, 'click', this._onClick, this); } var cursor = isMouseOverResizeArea ? this._resizeCursor : this._cursor; - L.DomUtil.setStyle(this._headerCanvas, 'cursor', cursor); + L.DomUtil.setStyle(this._canvas, 'cursor', cursor); this._hitResizeArea = isMouseOverResizeArea; } }, + + _onOutlineMouseEvent: function (e, eventHandler) { + // check if the group controls area has been hit + if (!this._hitOutline(e)) + return false; + + var pos = this._mouseEventToCanvasPos(this._canvas, e); + var level = this._getGroupLevel(this._getOrthogonalPos(pos)); + if (level < 0 || level >= this._groups.length) + return true; + + // when 2 collapsed group controls overlaps completely, + // clicking on the control should expand the lower/rightmost group + var groups = this._groups[level]; + var indexes = Object.keys(groups); + var len = indexes.length; + for (var i = len - 1; i >= 0; --i) { + e.group = groups[indexes[i]]; + if (eventHandler.call(this, e)) + break; + } + + return true; + }, + + _onGroupControlClick: function (e) { + var group = e.group; + if (!group) + return false; + + var pos = this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e)); + pos = pos - this._position; + if (group.startPos < pos && pos < group.startPos + this._groupHeadSize) { + this._updateOutlineState(/*isColumnOutline: */ this._isColumn, group); + return true; + } + return false; + }, + + _onDoubleClick: function (e) { + this._onOutlineMouseEvent(e, this._onGroupControlDoubleClick); + }, + + _onGroupControlDoubleClick: function (e) { + var group = e.group; + if (!group && !group.hidden) + return false; + + var pos = this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e)); + pos = pos - this._position; + if (group.startPos + this._groupHeadSize < pos && pos < group.endPos) { + this._updateOutlineState(/*isColumnOutline: */ this._isColumn, group); + return true; + } + return false; + }, + + _updateOutlineState: function (column, group) { + var e = { + x: this._map._getTopLeftPoint().x, + y: this._map._getTopLeftPoint().y, + offset: {x: undefined, y: undefined}, + outline: {column: column, level: group.level, index: group.index, hidden: !group.hidden} + }; + this._map.fire('updaterowcolumnheaders', e); + // TODO do we need this ? + //this._map._socket.sendMessage('commandvalues command=.uno:ViewAnnotationsPosition'); + }, + _onMouseDown: function (e) { - var target = e.target || e.srcElement; + if (this._hitOutline(e)) + return; + var target = e.target || e.srcElement; if (!target || this._dragging) { return false; } @@ -292,10 +410,11 @@ L.Control.Header = L.Control.extend({ L.DomEvent.stopPropagation(e); - L.DomEvent.off(target, 'mousemove', this._onCanvasMouseMove, this); + // disable normal mouse events + L.DomEvent.off(target, 'mousemove', this._onMouseMove, this); L.DomEvent.off(target, 'mouseout', this._onMouseOut, this); - - L.DomEvent.on(document, 'mousemove', this._onMouseMove, this); + // enable mouse events used on dragging + L.DomEvent.on(document, 'mousemove', this._onMouseMoveForDragging, this); L.DomEvent.on(document, 'mouseup', this._onMouseUp, this); var rect = this.getHeaderEntryBoundingClientRect(); @@ -306,7 +425,7 @@ L.Control.Header = L.Control.extend({ this.onDragStart(this.item, this._start, this._offset, e); }, - _onMouseMove: function (e) { + _onMouseMoveForDragging: function (e) { this._dragging = true; L.DomEvent.preventDefault(e); @@ -314,13 +433,14 @@ L.Control.Header = L.Control.extend({ }, _onMouseUp: function (e) { - L.DomEvent.off(document, 'mousemove', this._onMouseMove, this); + // disable mouse events used on dragging + L.DomEvent.off(document, 'mousemove', this._onMouseMoveForDragging, this); L.DomEvent.off(document, 'mouseup', this._onMouseUp, this); L.DomUtil.enableImageDrag(); L.DomUtil.enableTextSelection(); - - L.DomEvent.on(this._item, 'mousemove', this._onCanvasMouseMove, this); + // enable normal mouse events + L.DomEvent.on(this._item, 'mousemove', this._onMouseMove, this); L.DomEvent.on(this._item, 'mouseout', this._onMouseOut, this); if (this._dragging) { @@ -339,7 +459,181 @@ L.Control.Header = L.Control.extend({ if (!this.converter) return 0; var point = new L.Point(twips, twips); - return Math.round(this._getPos(this.converter(point))); + return Math.round(this._getParallelPos(this.converter(point))); + }, + + _setCanvasSizeImpl: function (container, canvas, property, value) { + if (!value) { + value = parseInt(L.DomUtil.getStyle(container, property)); + } + else { + L.DomUtil.setStyle(container, property, value + 'px'); + } + canvas[property] = value; + }, + + _setCanvasWidth: function (width) { + this._setCanvasSizeImpl(this._headerContainer, this._canvas, 'width', width); + }, + + _setCanvasHeight: function (height) { + this._setCanvasSizeImpl(this._headerContainer, this._canvas, 'height', height); + }, + + _setCornerCanvasWidth: function (width) { + this._setCanvasSizeImpl(this._cornerHeaderContainer, this._cornerCanvas, 'width', width); + }, + + _setCornerCanvasHeight: function (height) { + this._setCanvasSizeImpl(this._cornerHeaderContainer, this._cornerCanvas, 'height', height); + }, + + _hitOutline: function (e) { + var pos = this._mouseEventToCanvasPos(this._canvas, e); + return this._getOrthogonalPos(pos) <= this.getOutlineWidth(); + }, + + _getGroupLevel: function (pos) { + var levels = this._groups.length; + var size = this._levelSpacing + this._groupHeadSize; + + var level = (pos + 1) / size | 0; + var relPos = pos % size; + + if (level <= levels && relPos > this._levelSpacing) { + return level; + } + else { + return -1; + } + }, + + _getGroupLevelHeader: function (pos) { + if (!this._groups) + return; + + var levels = this._groups.length + 1; + var size = this._levelSpacing + this._groupHeadSize; + + var level = (pos + 1) / size | 0; + var relPos = pos % size; + + if (level < this._groups.length && relPos > this._levelSpacing) { + return level; + } + else { + return -1; + } + }, + + _computeOutlineWidth: function () { + return this._levelSpacing + (this._groupHeadSize + this._levelSpacing) * (this._groups.length + 1); + }, + + getOutlineWidth: function () { + if (this._isColumn) + return this._canvas.height - this._borderWidth - this._headerHeight; + else + return this._canvas.width - this._borderWidth - this._headerWidth; + }, + + _collectGroupsData: function(groups) { + var level, groupEntry; + + var lastGroupIndex = new Array(groups.length); + var firstChildGroupIndex = new Array(groups.length); + var lastLevel = -1; + for (var i = 0; i < groups.length; ++i) { + // a new group start + var groupData = groups[i]; + level = parseInt(groupData.level) - 1; + if (!this._groups[level]) { + this._groups[level] = {}; + } + var startPos = this._twipsToPixels(parseInt(groupData.startPos)); + var endPos = this._twipsToPixels(parseInt(groupData.endPos)); + var isHidden = !!parseInt(groupData.hidden); + if (isHidden) { + startPos -= this._groupHeadSize / 2; + endPos = startPos + this._groupHeadSize; + } + else { + var moved = false; + // if the first child is collapsed the parent head has to be top-aligned with the child + if (level < lastLevel && firstChildGroupIndex[lastLevel] !== undefined) { + var childGroupEntry = this._groups[lastLevel][firstChildGroupIndex[lastLevel]]; + if (childGroupEntry.hidden) { + if (startPos > childGroupEntry.startPos && startPos < childGroupEntry.endPos) { + startPos = childGroupEntry.startPos; + moved = true; + } + } + } + // if 2 groups belonging to the same level are contiguous and the first group is collapsed, + // the second one has to be shifted as much as possible in order to avoiding overlapping. + if (!moved && lastGroupIndex[level] !== undefined) { + var prevGroupEntry = this._groups[level][lastGroupIndex[level]]; + if (prevGroupEntry.hidden) { + if (startPos > prevGroupEntry.startPos && startPos < prevGroupEntry.endPos) { + startPos = prevGroupEntry.endPos; + } + } + } + } + groupEntry = { + level: level, + index: groupData.index, + startPos: startPos, + endPos: endPos, + hidden: isHidden + }; + this._groups[level][groupData.index] = groupEntry; + lastGroupIndex[level] = groupData.index; + if (level > lastLevel) { + firstChildGroupIndex[level] = groupData.index; + lastLevel = level; + } + else if (level === lastLevel) { + firstChildGroupIndex[level + 1] = undefined; + } + } + }, + + drawCornerHeader: function() { + var ctx = this._cornerCanvasContext; + + if (!this._groups) + return; + + ctx.save(); + ctx.fillStyle = this._borderColor; + if (this._isColumn) { + var startY = this._cornerCanvas.height - (L.Control.Header.colHeaderHeight + this._borderWidth); + if (startY > 0) + ctx.fillRect(0, startY, this._cornerCanvas.width, this._borderWidth); + } + else { + var startX = this._cornerCanvas.width - (L.Control.Header.rowHeaderWidth + this._borderWidth); + if (startX > 0) + ctx.fillRect(startX, 0, this._borderWidth, this._cornerCanvas.height); + } + ctx.restore(); + + var levels = this._groups.length + 1; + for (var i = 0; i < levels; ++i) { + this.drawLevelHeader(i); + } + }, + + drawOutline: function() { + if (this._groups) { + for (var itLevel = 0; itLevel < this._groups.length; ++itLevel) { + for (var groupIndex in this._groups[itLevel]) { + if (this._groups[itLevel].hasOwnProperty(groupIndex)) + this.drawGroupControl(this._groups[itLevel][groupIndex]); + } + } + } }, onDragStart: function () {}, @@ -348,10 +642,15 @@ L.Control.Header = L.Control.extend({ onDragClick: function () {}, getHeaderEntryBoundingClientRect: function () {}, drawHeaderEntry: function () {}, - _getPos: function () {} + drawGroupControl: function () {}, + _getParallelPos: function () {}, + _getOrthogonalPos: function () {} + }); (function () { + L.Control.Header.rowHeaderWidth = undefined; + L.Control.Header.colHeaderHeight = undefined; L.Control.Header.DataImpl = L.Class.extend({ initialize: function () { diff --git a/loleaflet/src/control/Control.Menubar.js b/loleaflet/src/control/Control.Menubar.js index fda37f4b..9ea8fcc9 100644 --- a/loleaflet/src/control/Control.Menubar.js +++ b/loleaflet/src/control/Control.Menubar.js @@ -311,6 +311,15 @@ L.Control.Menubar = L.Control.extend({ {name: _('Delete row'), type: 'unocommand', uno: '.uno:DeleteRows'}, {name: _('Delete column'), type: 'unocommand', uno: '.uno:DeleteColumns'}] }, + {name: _('Data'), type: 'menu', menu: [ + {name: _('Group'), type: 'unocommand', uno: '.uno:Group'}, + {name: _('Ungroup'), type: 'unocommand', uno: '.uno:Ungroup'}, + {type: 'separator'}, + {name: _('Remove Outline'), type: 'unocommand', uno: '.uno:ClearOutline'}, + {type: 'separator'}, + {name: _('Show Details'), type: 'unocommand', uno: '.uno:ShowDetail'}, + {name: _('Hide Details'), type: 'unocommand', uno: '.uno:HideDetail'}] + }, {name: _('Tools'), id: 'tools', type: 'menu', menu: [ {name: _('Automatic spell checking'), type: 'unocommand', uno: '.uno:SpellOnline'}, {name: _('Language'), type: 'menu', menu: [ diff --git a/loleaflet/src/control/Control.RowHeader.js b/loleaflet/src/control/Control.RowHeader.js index 4763f1b1..1eed7cd0 100644 --- a/loleaflet/src/control/Control.RowHeader.js +++ b/loleaflet/src/control/Control.RowHeader.js @@ -15,34 +15,39 @@ L.Control.RowHeader = L.Control.Header.extend({ _initialize: function () { this._initialized = true; + this._isColumn = false; this._map.on('scrolloffset', this.offsetScrollPosition, this); this._map.on('updatescrolloffset', this.setScrollPosition, this); this._map.on('viewrowcolumnheaders', this.viewRowColumnHeaders, this); this._map.on('updateselectionheader', this._onUpdateSelection, this); this._map.on('clearselectionheader', this._onClearSelection, this); this._map.on('updatecurrentheader', this._onUpdateCurrentRow, this); + this._map.on('updatecornerheader', this.drawCornerHeader, this); + this._map.on('cornerheaderclicked', this._onCornerHeaderClick, this); var rowColumnFrame = L.DomUtil.get('spreadsheet-row-column-frame'); - this._headersContainer = L.DomUtil.create('div', 'spreadsheet-header-rows-container', rowColumnFrame); - - this._headerCanvas = L.DomUtil.create('canvas', 'spreadsheet-header-rows', this._headersContainer); + this._headerContainer = L.DomUtil.createWithId('div', 'spreadsheet-header-rows-container', rowColumnFrame); this._initHeaderEntryStyles('spreadsheet-header-row'); this._initHeaderEntryHoverStyles('spreadsheet-header-row-hover'); this._initHeaderEntrySelectedStyles('spreadsheet-header-row-selected'); this._initHeaderEntryResizeStyles('spreadsheet-header-row-resize'); - this._canvasContext = this._headerCanvas.getContext('2d'); - this._headerCanvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width')); - this._headerCanvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height')); + this._canvas = L.DomUtil.create('canvas', 'spreadsheet-header-rows', this._headerContainer); + this._canvasContext = this._canvas.getContext('2d'); + this._setCanvasWidth(); + this._setCanvasHeight(); + this._headerWidth = this._canvas.width; + L.Control.Header.rowHeaderWidth = this._canvas.width; - L.DomUtil.setStyle(this._headerCanvas, 'cursor', this._cursor); + L.DomUtil.setStyle(this._canvas, 'cursor', this._cursor); - L.DomEvent.on(this._headerCanvas, 'mousemove', this._onCanvasMouseMove, this); - L.DomEvent.on(this._headerCanvas, 'mouseout', this._onMouseOut, this); - L.DomEvent.on(this._headerCanvas, 'click', this._onHeaderClick, this); + L.DomEvent.on(this._canvas, 'mousemove', this._onMouseMove, this); + L.DomEvent.on(this._canvas, 'mouseout', this._onMouseOut, this); + L.DomEvent.on(this._canvas, 'click', this._onClick, this); + L.DomEvent.on(this._canvas, 'dblclick', this._onDoubleClick, this); - this._topRow = 0; - this._topOffset = 0; + this._startHeaderIndex = 0; + this._startOffset = 0; this._position = 0; var rowHeaderObj = this; @@ -170,11 +175,13 @@ L.Control.RowHeader = L.Control.Header.extend({ }, _onUpdateCurrentRow: function (e) { - var y = e.y; + var y = e.min.y; + var h = e.getSize().y; if (y !== -1) { y = this._twipsToPixels(y); + h = this._twipsToPixels(h); } - this.updateCurrent(this._data, y); + this.updateCurrent(this._data, y, h); }, _updateRowHeader: function () { @@ -186,11 +193,12 @@ L.Control.RowHeader = L.Control.Header.extend({ return; var ctx = this._canvasContext; - 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 content = entry.index + this._startHeaderIndex; + var startOrt = this._canvas.width - this._headerWidth; + var startPar = entry.pos - entry.size - this._startOffset; + var endPar = entry.pos - this._startOffset; + var height = endPar - startPar; + var width = this._headerWidth; if (isHighlighted !== true && isHighlighted !== false) { isHighlighted = this.isHighlighted(entry.index); @@ -200,31 +208,118 @@ L.Control.RowHeader = L.Control.Header.extend({ return; ctx.save(); - ctx.translate(0, this._position + this._topOffset); + ctx.translate(0, this._position + this._startOffset); // background gradient var selectionBackgroundGradient = null; if (isHighlighted) { - selectionBackgroundGradient = ctx.createLinearGradient(0, start, 0, start + height); + selectionBackgroundGradient = ctx.createLinearGradient(0, startPar, 0, startPar + height); selectionBackgroundGradient.addColorStop(0, this._selectionBackgroundGradient[0]); selectionBackgroundGradient.addColorStop(0.5, this._selectionBackgroundGradient[1]); selectionBackgroundGradient.addColorStop(1, this._selectionBackgroundGradient[2]); } + + // draw header/outline border separator + if (this._headerWidth !== this._canvas.width) { + ctx.fillStyle = this._borderColor; + ctx.fillRect(startOrt - this._borderWidth, startPar, this._borderWidth, height); + } + // clip mask ctx.beginPath(); - ctx.rect(0, start, width, height); + ctx.rect(startOrt, startPar, width, height); ctx.clip(); // draw background ctx.fillStyle = isHighlighted ? selectionBackgroundGradient : isOver ? this._hoverColor : this._backgroundColor; - ctx.fillRect(0, start, width, height); + ctx.fillRect(startOrt, startPar, width, height); // draw text content ctx.fillStyle = isHighlighted ? this._selectionTextColor : this._textColor; ctx.font = this._font; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; - ctx.fillText(content, width / 2, end - (height / 2)); + ctx.fillText(content, startOrt + (width / 2), endPar - (height / 2)); // draw row separator ctx.fillStyle = this._borderColor; - ctx.fillRect(0, end -1, width, this._borderWidth); + ctx.fillRect(startOrt, endPar - 1, width , this._borderWidth); + ctx.restore(); + }, + + drawGroupControl: function (group) { + if (!group) + return; + + var ctx = this._canvasContext; + var headSize = this._groupHeadSize; + var spacing = this._levelSpacing; + var level = group.level; + + var startOrt = spacing + (headSize + spacing) * level; + var startPar = group.startPos - this._startOffset; + var height = group.endPos - group.startPos; + + ctx.save(); + ctx.translate(0, this._position + this._startOffset); + // clip mask + ctx.beginPath(); + ctx.rect(startOrt, startPar, headSize, height); + ctx.clip(); + if (!group.hidden) { + //draw tail + ctx.strokeStyle = 'black'; + ctx.lineWidth = 1.5; + ctx.beginPath(); + ctx.moveTo(startOrt + 2, startPar + headSize); + ctx.lineTo(startOrt + 2, startPar + height - 1); + ctx.lineTo(startOrt + 2 + headSize / 2, startPar + height - 1); + ctx.stroke(); + // draw head + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startOrt, startPar, headSize, headSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startOrt, startPar, headSize, headSize); + // draw '-' + ctx.lineWidth = 1; + ctx.strokeRect(startOrt + headSize / 4, startPar + headSize / 2, headSize / 2, 1); + } + else { + // draw head + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startOrt, startPar, headSize, headSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startOrt, startPar, headSize, headSize); + // draw '+' + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(startOrt + headSize / 4, startPar + headSize / 2); + ctx.lineTo(startOrt + 3 * headSize / 4, startPar + headSize / 2); + ctx.moveTo(startOrt + headSize / 2, startPar + headSize / 4); + ctx.lineTo(startOrt + headSize / 2, startPar + 3 * headSize / 4); + ctx.stroke(); + } + ctx.restore(); + }, + + drawLevelHeader: function(level) { + var ctx = this._cornerCanvasContext; + var ctrlHeadSize = this._groupHeadSize; + var levelSpacing = this._levelSpacing; + + var startOrt = levelSpacing + (ctrlHeadSize + levelSpacing) * level; + var startPar = this._cornerCanvas.height - (ctrlHeadSize + (L.Control.Header.colHeaderHeight - ctrlHeadSize) / 2); + + ctx.save(); + ctx.fillStyle = this._hoverColor; + ctx.fillRect(startOrt, startPar, ctrlHeadSize, ctrlHeadSize); + ctx.strokeStyle = 'black'; + ctx.lineWidth = 0.5; + ctx.strokeRect(startOrt, startPar, ctrlHeadSize, ctrlHeadSize); + // draw level number + ctx.fillStyle = this._textColor; + ctx.font = this._font; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(level + 1, startOrt + (ctrlHeadSize / 2), startPar + (ctrlHeadSize / 2)); ctx.restore(); }, @@ -236,7 +331,7 @@ L.Control.RowHeader = L.Control.Header.extend({ if (!entry) return; - var rect = this._headerCanvas.getBoundingClientRect(); + var rect = this._canvas.getBoundingClientRect(); var rowStart = entry.pos - entry.size + this._position; var rowEnd = entry.pos + this._position; @@ -250,23 +345,23 @@ L.Control.RowHeader = L.Control.Header.extend({ viewRowColumnHeaders: function (e) { if (e.data.rows && e.data.rows.length) { - this.fillRows(e.data.rows, e.converter, e.context); + this.fillRows(e.data.rows, e.data.rowGroups, e.converter, e.context); } }, - fillRows: function (rows, converter, context) { + fillRows: function (rows, rowGroups, converter, context) { if (rows.length < 2) return; - var entry, index, iterator, height, pos; + var headerEntry, 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')); + var canvas = this._canvas; + this._setCanvasWidth(); + this._setCanvasHeight(); this._canvasContext.clearRect(0, 0, canvas.width, canvas.height); // update first header index and reset no more valid variables - this._topRow = parseInt(rows[0].text); + this._startHeaderIndex = parseInt(rows[0].text); this._current = -1; this._selection.start = this._selection.end = -1; this._mouseOverEntry = null; @@ -279,8 +374,12 @@ L.Control.RowHeader = L.Control.Header.extend({ this.converter = L.Util.bind(converter, context); this._data.converter = L.Util.bind(this._twipsToPixels, this); + // create group array + this._groupLevels = parseInt(rows[0].groupLevels); + this._groups = this._groupLevels ? new Array(this._groupLevels) : null; + var startOffsetTw = parseInt(rows[0].size); - this._topOffset = this._twipsToPixels(startOffsetTw); + this._startOffset = this._twipsToPixels(startOffsetTw); this._data.pushBack(0, {pos: startOffsetTw, size: 0}); var prevPos = startOffsetTw; @@ -293,24 +392,39 @@ L.Control.RowHeader = L.Control.Header.extend({ 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); + index = index - this._startHeaderIndex; + headerEntry = {pos: pos, size: height}; + this._data.pushBack(index, headerEntry); } // setup last header entry - index = nextIndex - this._topRow; + index = nextIndex - this._startHeaderIndex; pos = parseInt(rows[last].size); height = pos - prevPos; this._data.pushBack(index, {pos: pos, size: height}); + // collect group controls data + if (rowGroups !== undefined && this._groups) { + this._collectGroupsData(rowGroups); + } + + if (this._groups) { + this.resize(this._computeOutlineWidth() + this._borderWidth + this._headerWidth); + } + else if (this._canvas.width !== this._headerWidth) { + this.resize(this._headerWidth); + } + // draw header - entry = this._data.getFirst(); - while (entry) { - this.drawHeaderEntry(entry, false); - entry = this._data.getNext(); + headerEntry = this._data.getFirst(); + while (headerEntry) { + this.drawHeaderEntry(headerEntry, false); + headerEntry = this._data.getNext(); } + // draw group controls + this.drawOutline(); + this.mouseInit(canvas); L.DomEvent.on(canvas, 'contextmenu', L.DomEvent.preventDefault); @@ -334,11 +448,14 @@ L.Control.RowHeader = L.Control.Header.extend({ this._map.sendUnoCommand('.uno:SelectRow ', command); }, - _onHeaderClick: function (e) { + _onClick: function (e) { + if (this._onOutlineMouseEvent(e, this._onGroupControlClick)) + return; + if (!this._mouseOverEntry) return; - var row = this._mouseOverEntry.index + this._topRow; + var row = this._mouseOverEntry.index + this._startHeaderIndex; var modifier = 0; if (e.shiftKey) { @@ -351,6 +468,18 @@ L.Control.RowHeader = L.Control.Header.extend({ this._selectRow(row, modifier); }, + _onCornerHeaderClick: function(e) { + var pos = this._mouseEventToCanvasPos(this._cornerCanvas, e); + + if (pos.x > this.getOutlineWidth()) { + // empty rectangle on the right select all + this._map.sendUnoCommand('.uno:SelectAll'); + } + + var level = this._getGroupLevel(pos.x); + this._updateOutlineState(/*is column: */ false, {column: false, level: level, index: -1}); + }, + _onDialogResult: function (e) { if (e.type === 'submit' && !isNaN(e.value)) { var extra = { @@ -399,7 +528,7 @@ L.Control.RowHeader = L.Control.Header.extend({ var clickedRow = this._mouseOverEntry; if (clickedRow) { var height = clickedRow.size; - var row = clickedRow.index + this._topRow; + var row = clickedRow.index + this._startHeaderIndex; if (this._data.isZeroSize(clickedRow.index + 1)) { row += 1; @@ -432,7 +561,7 @@ L.Control.RowHeader = L.Control.Header.extend({ return; if (clicks === 2) { - var row = this._mouseOverEntry.index + this._topRow; + var row = this._mouseOverEntry.index + this._startHeaderIndex; var command = { Row: { type: 'long', @@ -470,8 +599,33 @@ L.Control.RowHeader = L.Control.Header.extend({ } }, - _getPos: function (point) { + _getParallelPos: function (point) { return point.y; + }, + + _getOrthogonalPos: function (point) { + return point.x; + }, + + resize: function (width) { + if (width < this._headerWidth) + return; + + var columnHeader = L.DomUtil.get('spreadsheet-header-columns-container'); + var document = L.DomUtil.get('document-container'); + + this._setCornerCanvasWidth(width); + + var deltaLeft = width - this._canvas.width; + var colHdrLeft = parseInt(L.DomUtil.getStyle(columnHeader, 'left')) + deltaLeft; + var docLeft = parseInt(L.DomUtil.getStyle(document, 'left')) + deltaLeft; + console.log('resize: width: ' + width + ', deltaLeft: ' + deltaLeft + ', colHdrLeft: ' + colHdrLeft + ', docLeft: ' + docLeft); + L.DomUtil.setStyle(columnHeader, 'left', colHdrLeft + 'px'); + L.DomUtil.setStyle(document, 'left', docLeft + 'px'); + + this._setCanvasWidth(width); + + this._map.fire('updatecornerheader'); } }); diff --git a/loleaflet/src/control/Control.Scroll.js b/loleaflet/src/control/Control.Scroll.js index 06dfae20..b5e3b1ef 100644 --- a/loleaflet/src/control/Control.Scroll.js +++ b/loleaflet/src/control/Control.Scroll.js @@ -275,6 +275,11 @@ L.Control.Scroll = L.Control.extend({ var payload = 'commandvalues command=.uno:ViewRowColumnHeaders?x=' + Math.round(pos.x) + '&y=' + Math.round(pos.y) + '&width=' + Math.round(size.x) + '&height=' + Math.round(size.y); + if (e.outline) { + payload += '&columnOutline=' + e.outline.column + '&groupLevel=' + e.outline.level + + '&groupIndex=' + e.outline.index + '&groupHidden=' + e.outline.hidden; + } + this._map._socket.sendMessage(payload); } }); diff --git a/loleaflet/src/dom/DomUtil.js b/loleaflet/src/dom/DomUtil.js index 012c2574..a3e941a5 100644 --- a/loleaflet/src/dom/DomUtil.js +++ b/loleaflet/src/dom/DomUtil.js @@ -35,6 +35,18 @@ L.DomUtil = { return el; }, + createWithId: function (tagName, id, container) { + + var el = document.createElement(tagName); + el.id = id; + + if (container) { + container.appendChild(el); + } + + return el; + }, + remove: function (el) { var parent = el.parentNode; if (parent) { diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js b/loleaflet/src/layer/tile/CalcTileLayer.js index a863e4a1..b15cd5bb 100644 --- a/loleaflet/src/layer/tile/CalcTileLayer.js +++ b/loleaflet/src/layer/tile/CalcTileLayer.js @@ -356,10 +356,12 @@ L.CalcTileLayer = L.TileLayer.extend({ _onUpdateCurrentHeader: function() { var pos = new L.Point(-1, -1); + var size = new L.Point(-1, -1); if (this._cellCursor && !this._isEmptyRectangle(this._cellCursor)) { pos = this._cellCursorTwips.min.add([1, 1]); + size = this._cellCursorTwips.getSize(); } - this._map.fire('updatecurrentheader', pos); + this._map.fire('updatecurrentheader', new L.Bounds(pos, pos.add(size))); }, _onUpdateSelectionHeader: function () { _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits