[Libreoffice-commits] online.git: loleaflet/dist loleaflet/src
Marco Cecchetti
marco.cecchetti at collabora.com
Wed Nov 29 11:43:11 UTC 2017
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 550de386482c075e55fecffca4083eaa065ab076
Author: Marco Cecchetti <marco.cecchetti at 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/45360
Reviewed-by: Marco Cecchetti <mrcekets at gmail.com>
Tested-by: Marco Cecchetti <mrcekets at gmail.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 eb825cfe..28d5491f 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 () {
More information about the Libreoffice-commits
mailing list