[Libreoffice-commits] online.git: loleaflet/src
Marco Cecchetti
marco.cecchetti at collabora.com
Thu Oct 26 11:12:39 UTC 2017
loleaflet/src/control/Control.ColumnHeader.js | 267 ++++++++++++++++++--------
loleaflet/src/control/Control.Header.js | 169 ++++++++++++----
loleaflet/src/control/Control.RowHeader.js | 263 ++++++++++++++++++-------
3 files changed, 515 insertions(+), 184 deletions(-)
New commits:
commit 3dee70e3c4874abd6e7927b250d38d04d879c2bf
Author: Marco Cecchetti <marco.cecchetti at collabora.com>
Date: Thu Oct 26 12:00:05 2017 +0200
loleaflet: now Calc headers are rendered throught Canvas 2d
Change-Id: I5ab0d9a4af7a8bdba3eb0d07e89c811f7b1850b4
Reviewed-on: https://gerrit.libreoffice.org/43880
Reviewed-by: Marco Cecchetti <mrcekets at gmail.com>
Tested-by: Marco Cecchetti <mrcekets at gmail.com>
diff --git a/loleaflet/src/control/Control.ColumnHeader.js b/loleaflet/src/control/Control.ColumnHeader.js
index a9571d25..6d86d7fa 100644
--- a/loleaflet/src/control/Control.ColumnHeader.js
+++ b/loleaflet/src/control/Control.ColumnHeader.js
@@ -25,50 +25,76 @@ L.Control.ColumnHeader = L.Control.Header.extend({
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);
- var headersContainer = L.DomUtil.create('div', 'spreadsheet-header-columns-container', rowColumnFrame);
- this._columns = L.DomUtil.create('div', 'spreadsheet-header-columns', headersContainer);
+ this._headersContainer = L.DomUtil.create('div', 'spreadsheet-header-columns-container', rowColumnFrame);
+ 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'));
+
+ L.DomUtil.setStyle(this._headerCanvas, '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);
+
+ this._leftmostColumn = 0;
this._leftOffset = 0;
this._position = 0;
var colHeaderObj = this;
$.contextMenu({
- selector: '.spreadsheet-header-column-text',
+ selector: '.spreadsheet-header-columns',
className: 'loleaflet-font',
items: {
'insertcolbefore': {
name: _('Insert column before'),
callback: function(key, options) {
- var colAlpha = options.$trigger.attr('rel').split('spreadsheet-column-')[1];
- colHeaderObj.insertColumn.call(colHeaderObj, colAlpha);
+ var index = colHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var colAlpha = colHeaderObj._data[index].text;
+ colHeaderObj.insertColumn.call(colHeaderObj, colAlpha);
+ }
}
},
'deleteselectedcol': {
name: _('Delete column'),
callback: function(key, options) {
- var colAlpha = options.$trigger.attr('rel').split('spreadsheet-column-')[1];
- colHeaderObj.deleteColumn.call(colHeaderObj, colAlpha);
+ var index = colHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var colAlpha = colHeaderObj._data[index].text;
+ colHeaderObj.deleteColumn.call(colHeaderObj, colAlpha);
+ }
}
},
'optimalwidth': {
name: _('Optimal Width') + '...',
callback: function(key, options) {
- var colAlpha = options.$trigger.attr('rel').split('spreadsheet-column-')[1];
- colHeaderObj.optimalWidth.call(colHeaderObj, colAlpha);
+ var index = colHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var colAlpha = colHeaderObj._data[index].text;
+ colHeaderObj.optimalWidth.call(colHeaderObj, colAlpha);
+ }
}
},
'hideColumn': {
name: _('Hide Columns'),
callback: function(key, options) {
- var colAlpha = options.$trigger.attr('rel').split('spreadsheet-column-')[1];
- colHeaderObj.hideColumn.call(colHeaderObj, colAlpha);
+ var index = colHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var colAlpha = colHeaderObj._data[index].text;
+ colHeaderObj.hideColumn.call(colHeaderObj, colAlpha);
+ }
}
},
'showColumn': {
name: _('Show Columns'),
callback: function(key, options) {
- var colAlpha = options.$trigger.attr('rel').split('spreadsheet-column-')[1];
- colHeaderObj.showColumn.call(colHeaderObj, colAlpha);
+ var index = colHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var colAlpha = colHeaderObj._data[index].text;
+ colHeaderObj.showColumn.call(colHeaderObj, colAlpha);
+ }
}
}
},
@@ -136,71 +162,145 @@ L.Control.ColumnHeader = L.Control.Header.extend({
},
_onClearSelection: function (e) {
- this.clearSelection(this._columns);
+ this.clearSelection(this._data);
},
_onUpdateSelection: function (e) {
- this.updateSelection(this._columns, e.start.x, e.end.x);
+ var data = this._data;
+ if (!data)
+ return;
+ var start = e.start.x;
+ var end = e.end.x;
+ var twips;
+ if (start !== -1) {
+ twips = new L.Point(start, start);
+ start = Math.round(data.converter.call(data.context, twips).x);
+ }
+ if (end !== -1) {
+ twips = new L.Point(end, end);
+ end = Math.round(data.converter.call(data.context, twips).x);
+ }
+ this.updateSelection(data, start, end);
},
_onUpdateCurrentColumn: function (e) {
- this.updateCurrent(this._columns, e.x);
+ var data = this._data;
+ if (!data)
+ return;
+ var x = e.x;
+ if (x !== -1) {
+ var twips = new L.Point(x, x);
+ x = Math.round(data.converter.call(data.context, twips).x);
+ }
+ this.updateCurrent(data, x);
},
_updateColumnHeader: function () {
this._map.fire('updaterowcolumnheaders', {x: this._map._getTopLeftPoint().x, y: 0, offset: {x: undefined, y: 0}});
},
+ drawHeaderEntry: function (index, isOver) {
+ if (!index || index <= 0 || index >= this._data.length)
+ return;
+
+ var ctx = this._canvasContext;
+ var content = this._data[index].text;
+ var start = this._data[index - 1].pos - this._leftOffset;
+ var end = this._data[index].pos - this._leftOffset;
+ var width = end - start;
+ var height = this._headerCanvas.height;
+ var isHighlighted = this._data[index].selected;
+
+ if (width <= 0)
+ return;
+
+ ctx.save();
+ ctx.translate(this._position + this._leftOffset, 0);
+ // background gradient
+ var selectionBackgroundGradient = null;
+ if (isHighlighted) {
+ selectionBackgroundGradient = ctx.createLinearGradient(start, 0, start, height);
+ selectionBackgroundGradient.addColorStop(0, this._selectionBackgroundGradient[0]);
+ selectionBackgroundGradient.addColorStop(0.5, this._selectionBackgroundGradient[1]);
+ selectionBackgroundGradient.addColorStop(1, this._selectionBackgroundGradient[2]);
+ }
+ // clip mask
+ ctx.beginPath();
+ ctx.rect(start, 0, width, height);
+ ctx.clip();
+ // draw background
+ ctx.fillStyle = isHighlighted ? selectionBackgroundGradient : isOver ? this._hoverColor : this._backgroundColor;
+ ctx.fillRect(start, 0, 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);
+ // draw row separator
+ ctx.fillStyle = this._borderColor;
+ ctx.fillRect(end -1, 0, 1, height);
+ ctx.restore();
+ },
+
+ getHeaderEntryBoundingClientRect: function (index) {
+ if (!index)
+ index = this._mouseOverIndex; // use last mouse over position
+
+ if (!index || !this._data[index])
+ return;
+
+ var rect = this._headerCanvas.getBoundingClientRect();
+
+ var colStart = this._data[index - 1].pos + this._position;
+ var colEnd = this._data[index].pos + this._position;
+
+ var left = rect.left + colStart;
+ var right = rect.left + colEnd;
+ var top = rect.top;
+ var bottom = rect.bottom;
+ return { left: left, right: right, top: top, bottom: bottom };
+ },
+
viewRowColumnHeaders: function (e) {
if (e.data.columns && e.data.columns.length > 0) {
this.fillColumns(e.data.columns, e.converter, e.context);
- L.DomUtil.setStyle(this._columns, 'left', (this._position + this._leftOffset) + 'px');
}
},
fillColumns: function (columns, converter, context) {
- var iterator, twip, width, column, text, resize;
+ var iterator, twip, width;
+
+ this._data = new Array(columns.length);
+ this._data.converter = converter;
+ this._data.context = context;
+
+ var canvas = this._headerCanvas;
+ canvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width'));
+ canvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height'));
+
+ this._canvasContext.clearRect(0, 0, canvas.width, canvas.height);
+
+ var leftmostOffset = new L.Point(columns[0].size, columns[0].size);
+ this._leftmostColumn = parseInt(columns[0].text);
+ this._leftOffset = Math.round(converter.call(context, leftmostOffset).x);
+
+ this._data[0] = { pos: this._leftOffset, text: '', selected: false };
- L.DomUtil.empty(this._columns);
- var leftOffset = new L.Point(columns[0].size, columns[0].size);
- // column[0] is a dummy column header whose text attribute is set to the column index
- var leftmostCol = parseInt(columns[0].text);
- this._leftOffset = Math.round(converter.call(context, leftOffset).x);
for (iterator = 1; iterator < columns.length; iterator++) {
- width = columns[iterator].size - columns[iterator - 1].size;
- twip = new L.Point(width, width);
- column = L.DomUtil.create('div', 'spreadsheet-header-column', this._columns);
- text = L.DomUtil.create('div', 'spreadsheet-header-column-text', column);
- resize = L.DomUtil.create('div', 'spreadsheet-header-column-resize', column);
- L.DomEvent.on(resize, 'contextmenu', L.DomEvent.preventDefault);
- column.size = columns[iterator].size;
- var content = columns[iterator].text;
- text.setAttribute('rel', 'spreadsheet-column-' + content); // for easy addressing
- text.innerHTML = content;
- width = Math.round(converter.call(context, twip).x) - 1;
- if (width <= 0) {
- L.DomUtil.setStyle(column, 'display', 'none');
- } else if (width < 10) {
- text.column = iterator + leftmostCol;
- text.width = width;
- L.DomUtil.setStyle(column, 'width', width + 'px');
- L.DomUtil.setStyle(column, 'cursor', 'col-resize');
- L.DomUtil.setStyle(text, 'cursor', 'col-resize');
- L.DomUtil.setStyle(resize, 'display', 'none');
- this.mouseInit(text);
- } else {
- resize.column = iterator + leftmostCol;
- resize.width = width;
- L.DomUtil.setStyle(column, 'width', width + 'px');
- L.DomUtil.setStyle(text, 'width', width - 3 + 'px');
- L.DomUtil.setStyle(resize, 'width', '3px');
- this.mouseInit(resize);
+ twip = new L.Point(columns[iterator].size, columns[iterator].size);
+ this._data[iterator] = { pos: Math.round(converter.call(context, twip).x), text: columns[iterator].text, selected: false };
+ width = this._data[iterator].pos - this._data[iterator - 1].pos;
+ if (width > 0) {
+ this.drawHeaderEntry(iterator, false);
}
- L.DomEvent.addListener(text, 'click', this._onColumnHeaderClick, this);
}
- if ($('.spreadsheet-header-column-text').length > 0) {
- $('.spreadsheet-header-column-text').contextMenu(this._map._permission === 'edit');
+ this.mouseInit(canvas);
+
+ L.DomEvent.on(canvas, 'contextmenu', L.DomEvent.preventDefault);
+ if ($('.spreadsheet-header-columns').length > 0) {
+ $('.spreadsheet-header-columns').contextMenu(this._map._permission === 'edit');
}
},
@@ -232,8 +332,11 @@ L.Control.ColumnHeader = L.Control.Header.extend({
this._map.sendUnoCommand('.uno:SelectColumn ', command);
},
- _onColumnHeaderClick: function (e) {
- var colAlpha = e.target.getAttribute('rel').split('spreadsheet-column-')[1];
+ _onHeaderClick: function (e) {
+ if (!this._mouseOverIndex)
+ return;
+
+ var colAlpha = this._data[this._mouseOverIndex].text;
var modifier = 0;
if (e.shiftKey) {
@@ -295,21 +398,31 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var end = new L.Point(e.clientX + offset.x, e.clientY);
var distance = this._map._docLayer._pixelsToTwips(end.subtract(start));
- if (item.width != distance.x) {
- var command = {
- ColumnWidth: {
- type: 'unsigned short',
- value: this._map._docLayer.twipsToHMM(Math.max(distance.x, 0))
- },
- Column: {
- type: 'unsigned short',
- value: item.parentNode && item.parentNode.nextSibling &&
- L.DomUtil.getStyle(item.parentNode.nextSibling, 'display') === 'none' ? item.column + 1 : item.column
- }
- };
+ if (this._mouseOverIndex) {
+ var clickedColumn = this._data[this._mouseOverIndex];
+ var width = clickedColumn.pos - this._data[this._mouseOverIndex - 1];
+ var column = this._mouseOverIndex + this._leftmostColumn;
+
+ if (this._data[this._mouseOverIndex + 1]
+ && this._data[this._mouseOverIndex + 1].pos === clickedColumn.pos) {
+ column += 1;
+ }
+
+ if (width !== distance.x) {
+ var command = {
+ ColumnWidth: {
+ type: 'unsigned short',
+ value: this._map._docLayer.twipsToHMM(Math.max(distance.x, 0))
+ },
+ Column: {
+ type: 'unsigned short',
+ value: column
+ }
+ };
- this._map.sendUnoCommand('.uno:ColumnWidth', command);
- this._updateColumnHeader();
+ this._map.sendUnoCommand('.uno:ColumnWidth', command);
+ this._updateColumnHeader();
+ }
}
this._map.removeLayer(this._vertLine);
@@ -318,11 +431,15 @@ L.Control.ColumnHeader = L.Control.Header.extend({
onDragClick: function (item, clicks, e) {
this._map.removeLayer(this._vertLine);
+ if (!this._mouseOverIndex)
+ return;
+
if (clicks === 2) {
+ var column = this._mouseOverIndex + this._leftmostColumn;
var command = {
Col: {
type: 'unsigned short',
- value: item.column - 1
+ value: column - 1
},
Modifier: {
type: 'unsigned short',
@@ -343,9 +460,13 @@ L.Control.ColumnHeader = L.Control.Header.extend({
if (!this._initialized) {
this._initialize();
}
- if ($('.spreadsheet-header-column-text').length > 0) {
- $('.spreadsheet-header-column-text').contextMenu(e.perm === 'edit');
+ if ($('.spreadsheet-header-columns').length > 0) {
+ $('.spreadsheet-header-columns').contextMenu(e.perm === 'edit');
}
+ },
+
+ _getPos: function (point) {
+ return point.x;
}
});
diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js
index 7b309339..1c522caf 100644
--- a/loleaflet/src/control/Control.Header.js
+++ b/loleaflet/src/control/Control.Header.js
@@ -8,60 +8,77 @@ L.Control.Header = L.Control.extend({
},
initialize: function () {
+ this._headerCanvas = null;
this._clicks = 0;
this._current = -1;
this._selection = {start: -1, end: -1};
+ this._mouseOverIndex = undefined;
+ this._lastMouseOverIndex = undefined;
+ this._hitResizeArea = false;
+
+ // styles
+ this._backgroundColor = 'lightgray';
+ this._hoverColor = '#DDD';
+ this._borderColor = 'darkgray';
+ this._textColor = 'black';
+ this._font = '12px/1.5 "Segoe UI", Tahoma, Arial, Helvetica, sans-serif';
+ this._cursor = 'pointer';
+ this._selectionTextColor = 'white';
+ this._selectionBackgroundGradient = [ '#3465A4', '#729FCF', '#004586' ];
},
mouseInit: function (element) {
L.DomEvent.on(element, 'mousedown', this._onMouseDown, this);
},
- select: function (item) {
- if (item && !L.DomUtil.hasClass(item, 'spreadsheet-header-selected')) {
- L.DomUtil.addClass(item, 'spreadsheet-header-selected');
- }
+ select: function (data, index) {
+ if (!data[index])
+ return;
+ data[index].selected = true;
+ this.drawHeaderEntry(index, false);
},
- unselect: function (item) {
- if (item && L.DomUtil.hasClass(item, 'spreadsheet-header-selected')) {
- L.DomUtil.removeClass(item, 'spreadsheet-header-selected');
- }
+ unselect: function (data, index) {
+ if (!data[index])
+ return;
+ data[index].selected = false;
+ this.drawHeaderEntry(index, false);
},
- clearSelection: function (element) {
+ clearSelection: function (data) {
if (this._selection.start === -1 && this._selection.end === -1)
return;
- var childs = element.children;
var start = (this._selection.start === -1) ? 0 : this._selection.start;
var end = this._selection.end + 1;
for (var iterator = start; iterator < end; iterator++) {
- this.unselect(childs[iterator]);
+ this.unselect(data, iterator);
}
this._selection.start = this._selection.end = -1;
// after clearing selection, we need to select the header entry for the current cursor position,
// since we can't be sure that the selection clearing is due to click on a cell
// different from the one where the cursor is already placed
- this.select(childs[this._current]);
+ this.select(data, this._current);
},
- updateSelection: function(element, start, end) {
- var childs = element.children;
+ updateSelection: function(data, start, end) {
+ if (!data)
+ return;
+
var x0 = 0, x1 = 0;
var itStart = -1, itEnd = -1;
var selected = false;
var iterator = 0;
- for (var len = childs.length; iterator < len; iterator++) {
- x0 = (iterator > 0 ? childs[iterator - 1].size : 0);
- x1 = childs[iterator].size;
+ for (var len = data.length; iterator < len; iterator++) {
+ x0 = (iterator > 0 ? data[iterator - 1].pos : 0);
+ x1 = data[iterator].pos;
// 'start < x1' not '<=' or we get highlighted also the `start-row - 1` and `start-column - 1` headers
if (x0 <= start && start < x1) {
selected = true;
itStart = iterator;
}
if (selected) {
- this.select(childs[iterator]);
+ this.select(data, iterator);
}
if (x0 <= end && end <= x1) {
itEnd = iterator;
@@ -72,7 +89,7 @@ L.Control.Header = L.Control.extend({
// if end is greater than the last fetched header position set itEnd to the max possible value
// without this hack selecting a whole row and then a whole column (or viceversa) leads to an incorrect selection
if (itStart !== -1 && itEnd === -1) {
- itEnd = childs.length - 1;
+ itEnd = data.length - 1;
}
// we need to unselect the row (column) header entry for the current cell cursor position
@@ -80,43 +97,44 @@ L.Control.Header = L.Control.extend({
// does not start by clicking on a cell
if (this._current !== -1 && itStart !== -1 && itEnd !== -1) {
if (this._current < itStart || this._current > itEnd) {
- this.unselect(childs[this._current]);
+ this.unselect(data, this._current);
}
}
if (this._selection.start !== -1 && itStart !== -1 && itStart > this._selection.start) {
for (iterator = this._selection.start; iterator < itStart; iterator++) {
- this.unselect(childs[iterator]);
+ this.unselect(data, iterator);
}
}
if (this._selection.end !== -1 && itEnd !== -1 && itEnd < this._selection.end) {
for (iterator = itEnd + 1; iterator <= this._selection.end; iterator++) {
- this.unselect(childs[iterator]);
+ this.unselect(data, iterator);
}
}
this._selection.start = itStart;
this._selection.end = itEnd;
},
- updateCurrent: function (element, start) {
- var childs = element.children;
+ updateCurrent: function (data, start) {
+ if (!data)
+ return;
if (start < 0) {
- this.unselect(childs[this._current]);
+ this.unselect(data, this._current);
this._current = -1;
return;
}
var x0 = 0, x1 = 0;
- for (var iterator = 0, len = childs.length; iterator < len; iterator++) {
- x0 = (iterator > 0 ? childs[iterator - 1].size : 0);
- x1 = childs[iterator].size;
- if (x0 <= start && start <= x1) {
+ for (var iterator = 1, len = data.length; iterator < len; iterator++) {
+ x0 = (iterator > 0 ? data[iterator - 1].pos : 0);
+ x1 = data[iterator].pos;
+ if (x0 <= start && start < x1) {
// when a whole row (column) is selected the cell cursor is moved to the first column (row)
// but this action should not cause to select/unselect anything, on the contrary we end up
// with all column (row) header entries selected but the one where the cell cursor was
// previously placed
if (this._selection.start === -1 && this._selection.end === -1) {
- this.unselect(childs[this._current]);
- this.select(childs[iterator]);
+ this.unselect(data, this._current);
+ this.select(data, iterator);
}
this._current = iterator;
break;
@@ -124,6 +142,71 @@ L.Control.Header = L.Control.extend({
}
},
+ _mouseEventToCanvasPos: function(canvas, evt) {
+ var rect = canvas.getBoundingClientRect();
+ return {
+ x: evt.clientX - rect.left,
+ y: evt.clientY - rect.top
+ };
+ },
+
+ _onMouseOut: function (e) {
+ if (this._mouseOverIndex) {
+ this.drawHeaderEntry(this._mouseOverIndex, false);
+ this._lastMouseOverIndex = this._mouseOverIndex; // used by context menu
+ this._mouseOverIndex = undefined;
+ }
+ this._hitResizeArea = false;
+ L.DomUtil.setStyle(this._headerCanvas, 'cursor', this._cursor);
+ },
+
+ _onCanvasMouseMove: function (e) {
+ var target = e.target || e.srcElement;
+
+ if (!target || this._dragging) {
+ return false;
+ }
+
+ var isMouseOverResizeArea = false;
+ var pos = this._getPos(this._mouseEventToCanvasPos(this._headerCanvas, e));
+ pos = pos - this._position;
+
+ var mouseOverIndex = this._mouseOverIndex;
+ for (var iterator = 1; iterator < this._data.length; ++iterator) {
+ var start = this._data[iterator - 1].pos;
+ var end = this._data[iterator].pos;
+ if (pos > start && pos <= end) {
+ mouseOverIndex = iterator;
+ var resizeAreaStart = Math.max(start, end - 3);
+ isMouseOverResizeArea = (pos > resizeAreaStart);
+ break;
+ }
+ }
+
+ if (mouseOverIndex !== this._mouseOverIndex) {
+ if (this._mouseOverIndex) {
+ this.drawHeaderEntry(this._mouseOverIndex, false);
+ }
+ if (mouseOverIndex) {
+ this.drawHeaderEntry(mouseOverIndex, true);
+ }
+ }
+
+ if (isMouseOverResizeArea !== this._hitResizeArea) {
+ if (isMouseOverResizeArea) {
+ L.DomEvent.off(this._headerCanvas, 'click', this._onHeaderClick, this);
+ }
+ else {
+ L.DomEvent.on(this._headerCanvas, 'click', this._onHeaderClick, this);
+ }
+ var cursor = isMouseOverResizeArea ? this.options.cursor : this._cursor;
+ L.DomUtil.setStyle(this._headerCanvas, 'cursor', cursor);
+ this._hitResizeArea = isMouseOverResizeArea;
+ }
+
+ this._mouseOverIndex = mouseOverIndex;
+ },
+
_onMouseDown: function (e) {
var target = e.target || e.srcElement;
@@ -131,14 +214,21 @@ L.Control.Header = L.Control.extend({
return false;
}
+ if (!this._hitResizeArea)
+ return;
+
L.DomUtil.disableImageDrag();
L.DomUtil.disableTextSelection();
L.DomEvent.stopPropagation(e);
+
+ L.DomEvent.off(target, 'mousemove', this._onCanvasMouseMove, this);
+ L.DomEvent.off(target, 'mouseout', this._onMouseOut, this);
+
L.DomEvent.on(document, 'mousemove', this._onMouseMove, this);
L.DomEvent.on(document, 'mouseup', this._onMouseUp, this);
- var rect = target.parentNode.getBoundingClientRect();
+ var rect = this.getHeaderEntryBoundingClientRect();
this._start = new L.Point(rect.left, rect.top);
this._offset = new L.Point(rect.right - e.clientX, rect.bottom - e.clientY);
this._item = target;
@@ -150,13 +240,6 @@ L.Control.Header = L.Control.extend({
this._dragging = true;
L.DomEvent.preventDefault(e);
- var target = e.target || e.srcElement;
- if (target.style.cursor !== this.options.cursor &&
- (L.DomUtil.hasClass(target, 'spreadsheet-header-column-text') ||
- L.DomUtil.hasClass(target, 'spreadsheet-header-row-text'))) {
- target.style.cursor = this.options.cursor;
- }
-
this.onDragMove(this._item, this._start, this._offset, e);
},
@@ -167,6 +250,9 @@ L.Control.Header = L.Control.extend({
L.DomUtil.enableImageDrag();
L.DomUtil.enableTextSelection();
+ L.DomEvent.on(this._item, 'mousemove', this._onCanvasMouseMove, this);
+ L.DomEvent.on(this._item, 'mouseout', this._onMouseOut, this);
+
if (this._dragging) {
this.onDragEnd(this._item, this._start, this._offset, e);
this._clicks = 0;
@@ -182,5 +268,8 @@ L.Control.Header = L.Control.extend({
onDragStart: function () {},
onDragMove: function () {},
onDragEnd: function () {},
- onDragClick: function () {}
+ onDragClick: function () {},
+ getHeaderEntryBoundingClientRect: function () {},
+ drawHeaderEntry: function () {},
+ _getPos: function () {}
});
diff --git a/loleaflet/src/control/Control.RowHeader.js b/loleaflet/src/control/Control.RowHeader.js
index 744a99a6..99945f7d 100644
--- a/loleaflet/src/control/Control.RowHeader.js
+++ b/loleaflet/src/control/Control.RowHeader.js
@@ -22,50 +22,76 @@ L.Control.RowHeader = L.Control.Header.extend({
this._map.on('clearselectionheader', this._onClearSelection, this);
this._map.on('updatecurrentheader', this._onUpdateCurrentRow, this);
var rowColumnFrame = L.DomUtil.get('spreadsheet-row-column-frame');
- var headersContainer = L.DomUtil.create('div', 'spreadsheet-header-rows-container', rowColumnFrame);
- this._rows = L.DomUtil.create('div', 'spreadsheet-header-rows', headersContainer);
+ this._headersContainer = L.DomUtil.create('div', 'spreadsheet-header-rows-container', rowColumnFrame);
+ this._headerCanvas = L.DomUtil.create('canvas', 'spreadsheet-header-rows', 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'));
+
+ L.DomUtil.setStyle(this._headerCanvas, '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);
+
+ this._topRow = 0;
this._topOffset = 0;
this._position = 0;
var rowHeaderObj = this;
$.contextMenu({
- selector: '.spreadsheet-header-row-text',
+ selector: '.spreadsheet-header-rows',
className: 'loleaflet-font',
items: {
'insertrowabove': {
name: _('Insert row above'),
callback: function(key, options) {
- var row = parseInt(options.$trigger.attr('rel').split('spreadsheet-row-')[1]);
- rowHeaderObj.insertRow.call(rowHeaderObj, row);
+ var index = rowHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var row = rowHeaderObj._data[index].text;
+ rowHeaderObj.insertRow.call(rowHeaderObj, row);
+ }
}
},
'deleteselectedrow': {
name: _('Delete row'),
callback: function(key, options) {
- var row = parseInt(options.$trigger.attr('rel').split('spreadsheet-row-')[1]);
- rowHeaderObj.deleteRow.call(rowHeaderObj, row);
+ var index = rowHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var row = rowHeaderObj._data[index].text;
+ rowHeaderObj.deleteRow.call(rowHeaderObj, row);
+ }
}
},
'optimalheight': {
name: _('Optimal Height') + '...',
callback: function(key, options) {
- var row = parseInt(options.$trigger.attr('rel').split('spreadsheet-row-')[1]);
- rowHeaderObj.optimalHeight.call(rowHeaderObj, row);
+ var index = rowHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var row = rowHeaderObj._data[index].text;
+ rowHeaderObj.optimalHeight.call(rowHeaderObj, row);
+ }
}
},
'hideRow': {
name: _('Hide Rows'),
callback: function(key, options) {
- var row = parseInt(options.$trigger.attr('rel').split('spreadsheet-row-')[1]);
- rowHeaderObj.hideRow.call(rowHeaderObj, row);
+ var index = rowHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var row = rowHeaderObj._data[index].text;
+ rowHeaderObj.hideRow.call(rowHeaderObj, row);
+ }
}
},
'showRow': {
name: _('Show Rows'),
callback: function(key, options) {
- var row = parseInt(options.$trigger.attr('rel').split('spreadsheet-row-')[1]);
- rowHeaderObj.showRow.call(rowHeaderObj, row);
+ var index = rowHeaderObj._lastMouseOverIndex;
+ if (index) {
+ var row = rowHeaderObj._data[index].text;
+ rowHeaderObj.showRow.call(rowHeaderObj, row);
+ }
}
}
},
@@ -127,72 +153,145 @@ L.Control.RowHeader = L.Control.Header.extend({
},
_onClearSelection: function (e) {
- this.clearSelection(this._rows);
+ this.clearSelection(this._data);
},
_onUpdateSelection: function (e) {
- this.updateSelection(this._rows, e.start.y, e.end.y);
+ var data = this._data;
+ if (!data)
+ return;
+ var start = e.start.y;
+ var end = e.end.y;
+ var twips;
+ if (start !== -1) {
+ twips = new L.Point(start, start);
+ start = Math.round(data.converter.call(data.context, twips).y);
+ }
+ if (end !== -1) {
+ twips = new L.Point(end, end);
+ end = Math.round(data.converter.call(data.context, twips).y);
+ }
+ this.updateSelection(data, start, end);
},
_onUpdateCurrentRow: function (e) {
- this.updateCurrent(this._rows, e.y);
+ var data = this._data;
+ if (!data)
+ return;
+ var y = e.y;
+ if (y !== -1) {
+ var twips = new L.Point(y, y);
+ y = Math.round(data.converter.call(data.context, twips).y);
+ }
+ this.updateCurrent(data, y);
},
_updateRowHeader: function () {
this._map.fire('updaterowcolumnheaders', {x: 0, y: this._map._getTopLeftPoint().y, offset: {x: 0, y: undefined}});
},
+ drawHeaderEntry: function (index, isOver) {
+ if (!index || index <= 0 || index >= this._data.length)
+ return;
+
+ var ctx = this._canvasContext;
+ var content = this._data[index].text;
+ var start = this._data[index - 1].pos - this._topOffset;
+ var end = this._data[index].pos - this._topOffset;
+ var height = end - start;
+ var width = this._headerCanvas.width;
+ var isHighlighted = this._data[index].selected;
+
+ if (height <= 0)
+ return;
+
+ ctx.save();
+ ctx.translate(0, this._position + this._topOffset);
+ // background gradient
+ var selectionBackgroundGradient = null;
+ if (isHighlighted) {
+ selectionBackgroundGradient = ctx.createLinearGradient(0, start, 0, start + height);
+ selectionBackgroundGradient.addColorStop(0, this._selectionBackgroundGradient[0]);
+ selectionBackgroundGradient.addColorStop(0.5, this._selectionBackgroundGradient[1]);
+ selectionBackgroundGradient.addColorStop(1, this._selectionBackgroundGradient[2]);
+ }
+ // clip mask
+ ctx.beginPath();
+ ctx.rect(0, start, width, height);
+ ctx.clip();
+ // draw background
+ ctx.fillStyle = isHighlighted ? selectionBackgroundGradient : isOver ? this._hoverColor : this._backgroundColor;
+ ctx.fillRect(0, start, 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));
+ // draw row separator
+ ctx.fillStyle = this._borderColor;
+ ctx.fillRect(0, end -1, width, 1);
+ ctx.restore();
+ },
+
+ getHeaderEntryBoundingClientRect: function (index) {
+ if (!index)
+ index = this._mouseOverIndex; // use last mouse over position
+
+ if (!index || !this._data[index])
+ return;
+
+ var rect = this._headerCanvas.getBoundingClientRect();
+
+ var rowStart = this._data[index - 1].pos + this._position;
+ var rowEnd = this._data[index].pos + this._position;
+
+ var left = rect.left;
+ var right = rect.right;
+ var top = rect.top + rowStart;
+ var bottom = rect.top + rowEnd;
+ return { left: left, right: right, top: top, bottom: bottom };
+ },
+
viewRowColumnHeaders: function (e) {
if (e.data.rows && e.data.rows.length) {
this.fillRows(e.data.rows, e.converter, e.context);
- L.DomUtil.setStyle(this._rows, 'top', (this._position + this._topOffset) + 'px');
}
},
fillRows: function (rows, converter, context) {
- var iterator, twip, height, row, text, resize;
+ var iterator, twip, height;
+
+ this._data = new Array(rows.length);
+ this._data.converter = converter;
+ this._data.context = context;
+
+ var canvas = this._headerCanvas;
+ canvas.width = parseInt(L.DomUtil.getStyle(this._headersContainer, 'width'));
+ canvas.height = parseInt(L.DomUtil.getStyle(this._headersContainer, 'height'));
+
+ this._canvasContext.clearRect(0, 0, canvas.width, canvas.height);
- L.DomUtil.empty(this._rows);
var topOffset = new L.Point(rows[0].size, rows[0].size);
- var topRow = parseInt(rows[0].text);
+ this._topRow = parseInt(rows[0].text);
this._topOffset = Math.round(converter.call(context, topOffset).y);
+
+ this._data[0] = { pos: this._topOffset, text: '', selected: false };
+
for (iterator = 1; iterator < rows.length; iterator++) {
- height = rows[iterator].size - rows[iterator - 1].size;
- twip = new L.Point(height, height);
- row = L.DomUtil.create('div', 'spreadsheet-header-row', this._rows);
- text = L.DomUtil.create('div', 'spreadsheet-header-row-text', row);
- resize = L.DomUtil.create('div', 'spreadsheet-header-row-resize', row);
- L.DomEvent.on(resize, 'contextmenu', L.DomEvent.preventDefault);
- row.size = rows[iterator].size;
- var content = rows[iterator].text;
- text.setAttribute('rel', 'spreadsheet-row-' + content); // for easy addressing
- text.innerHTML = content;
- height = Math.round(converter.call(context, twip).y) - 1;
- if (height <= 0) {
- L.DomUtil.setStyle(row, 'display', 'none');
- } else if (height < 10) {
- text.row = iterator + topRow;
- text.height = height;
- L.DomUtil.setStyle(row, 'height', height + 'px');
- L.DomUtil.setStyle(row, 'cursor', 'row-resize');
- L.DomUtil.setStyle(text, 'line-height', height + 'px');
- L.DomUtil.setStyle(text, 'cursor', 'row-resize');
- L.DomUtil.setStyle(resize, 'display', 'none');
- this.mouseInit(text);
- } else {
- resize.row = iterator + topRow;
- resize.height = height;
- L.DomUtil.setStyle(row, 'height', height + 'px');
- L.DomUtil.setStyle(text, 'line-height', height - 3 + 'px');
- L.DomUtil.setStyle(text, 'height', height - 3 + 'px');
- L.DomUtil.setStyle(resize, 'height', '3px');
- this.mouseInit(resize);
+ twip = new L.Point(rows[iterator].size, rows[iterator].size);
+ this._data[iterator] = { pos: Math.round(converter.call(context, twip).y), text: rows[iterator].text, selected: false };
+ height = this._data[iterator].pos - this._data[iterator - 1].pos;
+ if (height > 0) {
+ this.drawHeaderEntry(iterator, false);
}
- L.DomEvent.addListener(text, 'click', this._onRowHeaderClick, this);
}
- if ($('.spreadsheet-header-row-text').length > 0) {
- $('.spreadsheet-header-row-text').contextMenu(this._map._permission === 'edit');
+ this.mouseInit(canvas);
+
+ L.DomEvent.on(canvas, 'contextmenu', L.DomEvent.preventDefault);
+ if ($('.spreadsheet-header-rows').length > 0) {
+ $('.spreadsheet-header-rows').contextMenu(this._map._permission === 'edit');
}
},
@@ -211,8 +310,12 @@ L.Control.RowHeader = L.Control.Header.extend({
this._map.sendUnoCommand('.uno:SelectRow ', command);
},
- _onRowHeaderClick: function (e) {
- var row = e.target.getAttribute('rel').split('spreadsheet-row-')[1];
+ _onHeaderClick: function (e) {
+ if (!this._mouseOverIndex)
+ return;
+
+ var row = this._mouseOverIndex + this._topRow;
+
var modifier = 0;
if (e.shiftKey) {
modifier += this._map.keyboard.keyModifier.shift;
@@ -269,20 +372,30 @@ L.Control.RowHeader = L.Control.Header.extend({
var end = new L.Point(e.clientX, e.clientY + offset.y);
var distance = this._map._docLayer._pixelsToTwips(end.subtract(start));
- if (item.height != distance.y) {
- var command = {
- RowHeight: {
- type: 'unsigned short',
- value: this._map._docLayer.twipsToHMM(Math.max(distance.y, 0))
- },
- Row: {
- type: 'long',
- value: item.parentNode && item.parentNode.nextSibling &&
- L.DomUtil.getStyle(item.parentNode.nextSibling, 'display') === 'none' ? item.row + 1 : item.row
- }
- };
+ if (this._mouseOverIndex) {
+ var clickedRow = this._data[this._mouseOverIndex];
+ var height = clickedRow.pos - this._data[this._mouseOverIndex - 1];
+ var row = this._mouseOverIndex + this._topRow;
+
+ if (this._data[this._mouseOverIndex + 1]
+ && this._data[this._mouseOverIndex + 1].pos === clickedRow.pos) {
+ row += 1;
+ }
+
+ if (height !== distance.y) {
+ var command = {
+ RowHeight: {
+ type: 'unsigned short',
+ value: this._map._docLayer.twipsToHMM(Math.max(distance.y, 0))
+ },
+ Row: {
+ type: 'long',
+ value: row
+ }
+ };
- this._map.sendUnoCommand('.uno:RowHeight', command);
+ this._map.sendUnoCommand('.uno:RowHeight', command);
+ }
}
this._map.removeLayer(this._horzLine);
@@ -291,11 +404,15 @@ L.Control.RowHeader = L.Control.Header.extend({
onDragClick: function (item, clicks, e) {
this._map.removeLayer(this._horzLine);
+ if (!this._mouseOverIndex)
+ return;
+
if (clicks === 2) {
+ var row = this._mouseOverIndex + this._topRow;
var command = {
Row: {
type: 'long',
- value: item.row - 1
+ value: row - 1
},
Modifier: {
type: 'unsigned short',
@@ -324,9 +441,13 @@ L.Control.RowHeader = L.Control.Header.extend({
this._initialize();
}
// Enable context menu on row headers only if permission is 'edit'
- if ($('.spreadsheet-header-row-text').length > 0) {
- $('.spreadsheet-header-row-text').contextMenu(e.perm === 'edit');
+ if ($('.spreadsheet-header-rows').length > 0) {
+ $('.spreadsheet-header-rows').contextMenu(e.perm === 'edit');
}
+ },
+
+ _getPos: function (point) {
+ return point.y;
}
});
More information about the Libreoffice-commits
mailing list