[Libreoffice-commits] online.git: loleaflet/src
Dennis Francis (via logerrit)
logerrit at kemper.freedesktop.org
Wed Jul 8 15:02:27 UTC 2020
loleaflet/src/control/Control.ColumnHeader.js | 77 +++--
loleaflet/src/control/Control.Header.js | 377 ++++++++++++++------------
loleaflet/src/control/Control.RowHeader.js | 77 +++--
loleaflet/src/layer/CalcGridLines.js | 28 -
loleaflet/src/layer/tile/CalcTileLayer.js | 3
5 files changed, 323 insertions(+), 239 deletions(-)
New commits:
commit 0b50166333219a5e111aa6e5344f940ead6ad628
Author: Dennis Francis <dennis.francis at collabora.com>
AuthorDate: Tue Jul 7 15:42:47 2020 +0530
Commit: Dennis Francis <dennis.francis at collabora.com>
CommitDate: Wed Jul 8 17:02:07 2020 +0200
make row/col headers render correctly for split-panes
For this to work, we need sheet-geometry data. GapTickMap is dropped
with a replacement HeaderInfo class that is easier/less confusing to
work with split-panes. All indices in HeaderInfo are 0-based and a
column/row is referred to as an 'element' when things have to be
generic.
Change-Id: Ibddac8901d48cada554b715af70195ef9b9832e2
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/98357
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>
Tested-by: Jenkins
Reviewed-by: Dennis Francis <dennis.francis at collabora.com>
diff --git a/loleaflet/src/control/Control.ColumnHeader.js b/loleaflet/src/control/Control.ColumnHeader.js
index 80559a20d..8a07f1e86 100644
--- a/loleaflet/src/control/Control.ColumnHeader.js
+++ b/loleaflet/src/control/Control.ColumnHeader.js
@@ -11,6 +11,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
onAdd: function (map) {
map.on('updatepermission', this._onUpdatePermission, this);
+ map.on('moveend zoomchanged sheetgeometrychanged splitposchanged', this._updateCanvas, this);
this._initialized = false;
},
@@ -159,6 +160,13 @@ L.Control.ColumnHeader = L.Control.Header.extend({
this._updateColumnHeader();
},
+ _updateCanvas: function () {
+ if (this._headerInfo) {
+ this._headerInfo.update();
+ this._redrawHeaders();
+ }
+ },
+
setScrollPosition: function (e) {
var position = -e.x;
this._position = Math.min(0, position);
@@ -186,7 +194,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
},
_onUpdateCurrentColumn: function (e) {
- var x = e.curX;
+ var x = e.curX - 1; // 1-based to 0-based.
var w = this._twipsToPixels(e.width);
var slim = w <= 1;
this.updateCurrent(x, slim);
@@ -201,10 +209,10 @@ L.Control.ColumnHeader = L.Control.Header.extend({
return;
var ctx = this._canvasContext;
- var content = this._colIndexToAlpha(entry.index);
+ var content = this._colIndexToAlpha(entry.index + 1);
var startOrt = this._canvasHeight - this._headerHeight;
- var startPar = entry.pos - entry.size - this._startOffset;
- var endPar = entry.pos - this._startOffset;
+ var startPar = entry.pos - entry.size;
+ var endPar = entry.pos;
var width = endPar - startPar;
var height = this._headerHeight;
@@ -218,7 +226,6 @@ L.Control.ColumnHeader = L.Control.Header.extend({
ctx.save();
var scale = L.getDpiScaleFactor();
ctx.scale(scale, scale);
- ctx.translate(this._position + this._startOffset, 0);
// background gradient
var selectionBackgroundGradient = null;
if (isHighlighted) {
@@ -279,14 +286,13 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var level = group.level;
var startOrt = spacing + (headSize + spacing) * level;
- var startPar = group.startPos - this._startOffset;
+ var startPar = this._headerInfo.docToHeaderPos(group.startPos);
var height = group.endPos - group.startPos;
ctx.save();
var scale = L.getDpiScaleFactor();
ctx.scale(scale, scale);
- ctx.translate(this._position + this._startOffset, 0);
// clip mask
ctx.beginPath();
ctx.rect(startPar, startOrt, height, headSize);
@@ -357,7 +363,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
getHeaderEntryBoundingClientRect: function (index) {
var entry = this._mouseOverEntry;
if (index) {
- entry = this._tickMap.getGap(index);
+ entry = this._headerInfo.getColData(index);
}
if (!entry)
@@ -365,8 +371,8 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var rect = this._canvas.getBoundingClientRect();
- var colStart = entry.pos - entry.size + this._position;
- var colEnd = entry.pos + this._position;
+ var colStart = entry.pos - entry.size;
+ var colEnd = entry.pos;
var left = rect.left + colStart;
var right = rect.left + colEnd;
@@ -408,11 +414,12 @@ L.Control.ColumnHeader = L.Control.Header.extend({
}
var sheetGeometry = this._map._docLayer.sheetGeometry;
- var columnsGeometry = sheetGeometry ? sheetGeometry.getColumnsGeometry() : undefined;
- // create data structure for column widths
- this._tickMap = new L.Control.Header.GapTickMap(this._map, columns, columnsGeometry);
- this._startOffset = this._tickMap.getStartOffset();
+ if (!this._headerInfo) {
+ // create data structure for column widths
+ this._headerInfo = new L.Control.Header.HeaderInfo(this._map, true /* isCol */);
+ this._map._colHdr = this._headerInfo;
+ }
// setup conversion routine
this.converter = L.Util.bind(converter, context);
@@ -437,13 +444,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
this.resize(this._headerHeight);
}
- // Initial draw
- this._tickMap.forEachGap(function(gap) {
- this.drawHeaderEntry(gap, false);
- }.bind(this));
-
- // draw group controls
- this.drawOutline();
+ this._redrawHeaders();
this.mouseInit(canvas);
@@ -452,6 +453,16 @@ L.Control.ColumnHeader = L.Control.Header.extend({
}
},
+ _redrawHeaders: function () {
+ this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height);
+ this._headerInfo.forEachElement(function(elemData) {
+ this.drawHeaderEntry(elemData, false);
+ }.bind(this));
+
+ // draw group controls
+ this.drawOutline();
+ },
+
_colAlphaToNumber: function(alpha) {
var res = 0;
var offset = 'A'.charCodeAt();
@@ -482,7 +493,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var command = {
Col: {
type: 'unsigned short',
- value: colNumber - 1
+ value: colNumber
},
Modifier: {
type: 'unsigned short',
@@ -549,11 +560,18 @@ L.Control.ColumnHeader = L.Control.Header.extend({
},
_getVertLatLng: function (start, offset, e) {
- var limit = this._map.mouseEventToContainerPoint({clientX: start.x, clientY: start.y});
+ var size = this._map.getSize();
var drag = this._map.mouseEventToContainerPoint(e);
+ var entryStart = this._dragEntry.pos - this._dragEntry.size;
+ var xdocpos = this._headerInfo.headerToDocPos(Math.max(drag.x, entryStart));
+ var ymin = this._map.getPixelBounds().min.y;
+ var ymax = ymin + size.y;
+ if (this._headerInfo.hasSplits()) {
+ ymin = 0;
+ }
return [
- this._map.containerPointToLatLng(new L.Point(Math.max(limit.x, drag.x + offset.x), 0)),
- this._map.containerPointToLatLng(new L.Point(Math.max(limit.x, drag.x + offset.x), this._map.getSize().y))
+ this._map.unproject(new L.Point(xdocpos, ymin)),
+ this._map.unproject(new L.Point(xdocpos, ymax)),
];
},
@@ -583,8 +601,9 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var width = clickedColumn.size;
var column = clickedColumn.index;
- if (this._tickMap.isZeroSize(clickedColumn.index + 1)) {
- column += 1;
+ var nextCol = this._headerInfo.getNextIndex(clickedColumn.index);
+ if (this._headerInfo.isZeroSize(nextCol)) {
+ column = nextCol;
width = 0;
}
@@ -596,7 +615,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
},
Column: {
type: 'unsigned short',
- value: column
+ value: column + 1 // core expects 1-based index.
}
};
@@ -619,7 +638,7 @@ L.Control.ColumnHeader = L.Control.Header.extend({
var command = {
Col: {
type: 'unsigned short',
- value: column - 1
+ value: column
},
Modifier: {
type: 'unsigned short',
diff --git a/loleaflet/src/control/Control.Header.js b/loleaflet/src/control/Control.Header.js
index 3ef4bb2e0..8dff63acc 100644
--- a/loleaflet/src/control/Control.Header.js
+++ b/loleaflet/src/control/Control.Header.js
@@ -172,17 +172,17 @@ L.Control.Header = L.Control.extend({
clearSelection: function () {
if (this._selection.start === -1 && this._selection.end === -1)
return;
- var start = (this._selection.start < 1) ? 1 : this._selection.start;
- var end = this._selection.end + 1;
+ var start = (this._selection.start < 1) ? 0 : this._selection.start;
+ var end = this._headerInfo.getNextIndex(this._selection.end);
- for (var i=start; i<end; i++) {
+ for (var i = start; i < end; i = this._headerInfo.getNextIndex(i)) {
if (i === this._current) {
// 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(this._tickMap.getGap(i), true);
+ this.select(this._headerInfo.getElementData(i), true);
} else {
- this.unselect(this._tickMap.getGap(i));
+ this.unselect(this._headerInfo.getElementData(i));
}
}
@@ -193,25 +193,28 @@ L.Control.Header = L.Control.extend({
// selects the new set of rows/cols.
// Start and end are given in pixels absolute to the document
updateSelection: function(start, end) {
- if (!this._tickMap)
+ if (!this._headerInfo)
return;
+ start = this._headerInfo.docToHeaderPos(start);
+ end = this._headerInfo.docToHeaderPos(end);
+
var x0 = 0, x1 = 0;
var itStart = -1, itEnd = -1;
// if the start selection position is above/on the left of the first header entry,
// but the end selection position is below/on the right of it
// then we set the start selected entry to the first header entry.
- var entry = this._tickMap.getGap(this._tickMap.getMinTickIdx() + 1);
+ var entry = this._headerInfo.getElementData(this._headerInfo.getMinIndex());
if (entry) {
x0 = entry.pos - entry.size;
if (start < x0 && end > x0) {
- itStart = 1;
+ itStart = 0;
}
}
- this._tickMap.forEachGap((function(entry) {
- x0 = entry.start;
+ this._headerInfo.forEachElement((function(entry) {
+ x0 = entry.pos - entry.size;
x1 = entry.pos;
if (start < x1 && end > x0) {
this.select(entry, false);
@@ -221,7 +224,7 @@ L.Control.Header = L.Control.extend({
} else {
this.unselect(entry);
if (itStart !== -1 && itEnd === -1) {
- itEnd = entry.index - 1;
+ itEnd = this._headerInfo.getPreviousIndex(entry.index);
}
}
}).bind(this));
@@ -229,7 +232,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 = this._tickMap.getMaxTickIdx() - 1;
+ itEnd = this._headerInfo.getMaxIndex();
}
this._selection.start = itStart;
@@ -240,24 +243,24 @@ L.Control.Header = L.Control.extend({
// Called whenever the cell cursor is in a cell corresponding to the cursorPos-th
// column/row.
updateCurrent: function (cursorPos, slim) {
- if (!this._tickMap) {return;}
+ if (!this._headerInfo) {return;}
if (cursorPos < 0) {
- this.unselect(this._tickMap.getGap(this._current));
+ this.unselect(this._headerInfo.getElementData(this._current));
this._current = -1;
return;
}
- var prevEntry = cursorPos > 0 ? this._tickMap.getGap(cursorPos - 1) : null;
+ var prevEntry = cursorPos > 0 ? this._headerInfo.getPreviousIndex(cursorPos) : null;
var zeroSizeEntry = slim && prevEntry && prevEntry.size === 0;
- var entry = this._tickMap.getGap(cursorPos);
+ var entry = this._headerInfo.getElementData(cursorPos);
if (this._selection.start === -1 && this._selection.end === -1) {
// 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
- this.unselect(this._tickMap.getGap(this._current));
+ this.unselect(this._headerInfo.getElementData(this._current));
// no selection when the cell cursor is slim
if (entry && !zeroSizeEntry)
this.select(entry, true);
@@ -280,22 +283,23 @@ L.Control.Header = L.Control.extend({
},
_entryAtPoint: function(point) {
- if (!this._tickMap)
+ if (!this._headerInfo)
return false;
var position = this._getParallelPos(point);
- position = position - this._position;
var that = this;
var result = null;
- this._tickMap.forEachGap(function(gap) {
- if (position >= gap.start && position < gap.end) {
- var resizeAreaStart = Math.max(gap.start, gap.end - 3);
- if (that.isHeaderSelected(gap.index)) {
- resizeAreaStart = gap.end - that._resizeHandleSize;
+ this._headerInfo.forEachElement(function(entry) {
+ var end = entry.pos;
+ var start = end - entry.size;
+ if (position >= start && position < end) {
+ var resizeAreaStart = Math.max(start, end - 3);
+ if (that.isHeaderSelected(entry.index)) {
+ resizeAreaStart = end - that._resizeHandleSize;
}
var isMouseOverResizeArea = (position > resizeAreaStart);
- result = {entry: gap, hit: isMouseOverResizeArea};
+ result = {entry: entry, hit: isMouseOverResizeArea};
return true;
}
});
@@ -325,6 +329,7 @@ L.Control.Header = L.Control.extend({
this._start = new L.Point(rectangle.left, rectangle.top);
this._offset = new L.Point(rectangle.right - event.center.x, rectangle.bottom - event.center.y);
this._item = target;
+ this._dragEntry = result.entry;
this.onDragStart(this._item, this._start, this._offset, {clientX: event.center.x, clientY: event.center.y});
},
@@ -337,6 +342,7 @@ L.Control.Header = L.Control.extend({
L.DomUtil.enableTextSelection();
this.onDragEnd(this._item, this._start, this._offset, {clientX: event.center.x, clientY: event.center.y});
+ this._dragEntry = null;
this._mouseOverEntry = null;
this._item = this._start = this._offset = null;
@@ -402,7 +408,7 @@ L.Control.Header = L.Control.extend({
entry = result.entry;
}
- if (mouseOverIndex && (!this._mouseOverEntry || mouseOverIndex !== this._mouseOverEntry.index)) {
+ if (typeof mouseOverIndex === 'number' && (!this._mouseOverEntry || mouseOverIndex !== this._mouseOverEntry.index)) {
var mouseOverIsCurrent = false;
if (this._mouseOverEntry != null) {
mouseOverIsCurrent = (this._mouseOverEntry.index == this._current);
@@ -456,8 +462,8 @@ L.Control.Header = L.Control.extend({
if (!group)
return false;
- var pos = this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e));
- pos = pos - this._position;
+ var pos = this._headerInfo.headerToDocPos(
+ this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e)));
if (group.startPos < pos && pos < group.startPos + this._groupHeadSize) {
this._updateOutlineState(/*isColumnOutline: */ this._isColumn, group);
return true;
@@ -474,8 +480,8 @@ L.Control.Header = L.Control.extend({
if (!group && !group.hidden)
return false;
- var pos = this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e));
- pos = pos - this._position;
+ var pos = this._headerInfo.headerToDocPos(
+ this._getParallelPos(this._mouseEventToCanvasPos(this._canvas, e)));
if (group.startPos + this._groupHeadSize < pos && pos < group.endPos) {
this._updateOutlineState(/*isColumnOutline: */ this._isColumn, group);
return true;
@@ -525,6 +531,7 @@ L.Control.Header = L.Control.extend({
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;
+ this._dragEntry = this._mouseOverEntry;
this.onDragStart(this._item, this._start, this._offset, e);
},
@@ -561,6 +568,7 @@ L.Control.Header = L.Control.extend({
this._item = this._start = this._offset = null;
this._dragging = false;
+ this._dragEntry = null;
},
_twipsToPixels: function (twips) {
@@ -771,165 +779,208 @@ L.Control.Header = L.Control.extend({
L.Control.Header.rowHeaderWidth = undefined;
L.Control.Header.colHeaderHeight = undefined;
-/*
- * GapTickMap is a data structure for handling the dimensions of each
- * column/row in the column/row header.
- *
- * A "tick" is the position of the boundary between two cols/rows, a "gap" is
- * the space between two ticks - the position and width/height of a col/row.
- *
- * Data about ticks is 0-indexed: the first tick (top of row 1 / left of column A) is 0.
- *
- * Data about gaps is 1-indexed: The 1st gap (row 1 / column A) is 1, and spans
- * from tick 0 to tick 1.
- *
- * A GapTickMap is fed data coming from a 'viewrowcolumnheaders' UNO message.
- * That contains the position of some of the ticks. The rest of the tick positions
- * is extrapolated as follows:
- * - The first two ticks are assumed to be consecutive. This sets the size of
- * the first gap.
- * - If the position of n-th tick is not explicit, then its position is the (n-1)-th tick plus
- * the size of the last known gap.
- * - When a new tick position is defined, it resets the size of the last known gap
- *
- * All inputs received are given in tile pixels, and stored as such.
- * outputs are returned in CSS pixels.
- *
- * **NB.** CSS pixels are scaled (down) from the tile pixels by the a factor
- * from TileLayer - 2x for retina, 1x for non-retina.
- *
- * **NB.** twip to pixel mapping is made non-obvious by the need to ensure that
- * there are no cumulative rounding errors from TWIP heights to pixels. We have to
- * match the core here, so we just use pixels.
- */
-L.Control.Header.GapTickMap = L.Class.extend({
-
- initialize: function (map, ticks, dimensionGeometry) {
-
- if (dimensionGeometry) {
- // Until .uno:ViewRowColumnHeaders is not phased out, we need to live with
- // GapTickMap datastructure to avoid an invasive refactoring.
- // L.SheetGeometry and L.SheetDimension datastructures can directly provide
- // position/size of any row/column intuitively without using unnecessary
- // terminologies like (1-based) Gap and (0-based) Tick.
- var dimrange = dimensionGeometry.getViewElementRange();
- var start = Math.max(0, dimrange.start - 2);
- var startData = dimensionGeometry.getElementData(start);
- var startText = start ? start + 1 : 0;
- var endText = Math.min(dimensionGeometry.getMaxIndex(), dimrange.end + 2) + 1;
-
- this._minTickIdx = startText;
- this._maxTickIdx = endText;
- this._startOffset = start ? startData.startpos + startData.size : 0;
- this._tilePixelScale = 1; // We already have everything in css px.
-
- ticks = start ? [] : [0];
- dimensionGeometry.forEachInRange(start,
- this._maxTickIdx - 1, function (idx, data) {
- ticks[idx + 1] = data.startpos + data.size;
- });
-
- this._ticks = ticks;
-
- return;
+L.Control.Header.HeaderInfo = L.Class.extend({
+
+ initialize: function (map, isCol) {
+ console.assert(map && isCol !== undefined, 'map and isCol required');
+ this._map = map;
+ this._isCol = isCol;
+ console.assert(this._map._docLayer.sheetGeometry, 'no sheet geometry data-structure found!');
+ var sheetGeom = this._map._docLayer.sheetGeometry;
+ this._dimGeom = this._isCol ? sheetGeom.getColumnsGeometry() : sheetGeom.getRowsGeometry();
+ this.update();
+ },
+
+ update: function () {
+ var bounds = this._map.getPixelBounds();
+ var startPx = this._isCol ? bounds.getTopLeft().x : bounds.getTopLeft().y;
+ this._docVisStart = startPx;
+ var endPx = this._isCol ? bounds.getBottomRight().x : bounds.getBottomRight().y;
+ var startIdx = this._dimGeom.getIndexFromPos(startPx, 'csspixels');
+ var endIdx = this._dimGeom.getIndexFromPos(endPx - 1, 'csspixels');
+ this._elements = [];
+
+ var splitPosContext = this._map.getSplitPanesContext();
+
+ this._hasSplits = false;
+ this._splitIndex = 0;
+ var splitPos = 0;
+
+ if (splitPosContext) {
+
+ splitPos = this._isCol ? splitPosContext.getSplitPos().x : splitPosContext.getSplitPos().y;
+ var splitIndex = this._dimGeom.getIndexFromPos(splitPos + 1, 'csspixels');
+
+ if (splitIndex) {
+ // Make sure splitPos is aligned to the cell boundary.
+ splitPos = this._dimGeom.getElementData(splitIndex).startpos;
+ this._splitPos = splitPos;
+ this._dimGeom.forEachInRange(0, splitIndex - 1,
+ function (idx, data) {
+ this._elements[idx] = {
+ index: idx,
+ pos: data.startpos + data.size, // end position on the header canvas
+ size: data.size,
+ origsize: data.size,
+ };
+ }.bind(this)
+ );
+
+ this._hasSplits = true;
+ this._splitIndex = splitIndex;
+
+ var freeStartPos = startPx + splitPos + 1;
+ var freeStartIndex = this._dimGeom.getIndexFromPos(freeStartPos + 1, 'csspixels');
+
+ startIdx = freeStartIndex;
+ }
}
- var gapSize;
- this._ticks = [];
+ // first free index
+ var dataFirstFree = this._dimGeom.getElementData(startIdx);
+ var firstFreeEnd = dataFirstFree.startpos + dataFirstFree.size - startPx;
+ var firstFreeStart = splitPos;
+ var firstFreeSize = Math.max(0, firstFreeEnd - firstFreeStart);
+ this._elements[startIdx] = {
+ index: startIdx,
+ pos: firstFreeEnd, // end position on the header canvas
+ size: firstFreeSize,
+ origsize: dataFirstFree.size,
+ };
- // Sanitize input
- var knownTicks = [];
- for (var i in ticks) {
- // The field in the input data struct is called "text" but it's the
- // column/row index, as a string.
- // Idem for "size": it's the tick position in pixels, as a string
- knownTicks[ parseInt(ticks[i].text) ] = parseInt(ticks[i].size);
- }
+ this._dimGeom.forEachInRange(startIdx + 1,
+ endIdx, function (idx, data) {
+ var startpos = data.startpos - startPx;
+ var endpos = startpos + data.size;
+ var size = endpos - startpos;
+ this._elements[idx] = {
+ index: idx,
+ pos: endpos, // end position on the header canvas
+ size: size,
+ origsize: size,
+ };
+ }.bind(this));
- // This *assumes* the input is ordered - i.e. tick indexes only grow
- this._minTickIdx = parseInt(ticks[0].text);
- this._maxTickIdx = knownTicks.length - 1;
- this._startOffset = parseInt(ticks[0].size);
- this._tilePixelScale = map._docLayer._tilePixelScale;
+ this._startIndex = startIdx;
+ this._endIndex = endIdx;
+ },
- for (var idx=this._minTickIdx; idx <= this._maxTickIdx; idx++) {
+ docToHeaderPos: function (docPos) {
- if (idx in knownTicks) {
- this._ticks[idx] = knownTicks[idx];
- gapSize = this._ticks[idx] - this._ticks[idx - 1];
- } else {
- if (isNaN(gapSize) || gapSize < 0) {
- // This should never happen, unless data from the UNO message
- // is not in strictly increasing order, or the first two ticks
- // are not consecutive.
- throw new Error('Malformed data for column/row sizes.');
- }
- this._ticks[idx] = this._ticks[idx - 1] + gapSize;
- }
+ if (!this._hasSplits) {
+ return docPos - this._docVisStart;
}
+
+ if (docPos <= this._splitPos) {
+ return docPos;
+ }
+
+ // max here is to prevent encroachment of the fixed pane-area.
+ return Math.max(docPos - this._docVisStart, this._splitPos);
},
- // Gets the position of the i-th tick (or `undefined` if the index falls outside).
- getTick: function getTick(i) {
- // to get CSS pixels we need to adjust for DPI scale
- // since we render at full native pixel resolution &
- // account in those units.
- return this._ticks[i] / this._tilePixelScale;
+ headerToDocPos: function (hdrPos) {
+ if (!this._hasSplits) {
+ return hdrPos + this._docVisStart;
+ }
+
+ if (hdrPos <= this._splitPos) {
+ return hdrPos;
+ }
+
+ return hdrPos + this._docVisStart;
},
getStartOffset: function() {
- return this._startOffset / this._tilePixelScale;
+ return 0;
},
- getMinTickIdx: function() {
- return this._minTickIdx;
+ isZeroSize: function (i) {
+ var elem = this._elements[i];
+ console.assert(elem, 'queried a non existent row/col in the header : ' + i);
+ return elem.size === 0;
},
- getMaxTickIdx: function() {
- return this._maxTickIdx;
+
+ hasSplits: function () {
+ return this._hasSplits;
},
- // Gets the start and size of the i-th gap.
- // returns an Object of the form {index: i, pos: start, size: width/height },
- // or `undefined` if the index falls outside.
- getGap: function getGap(i) {
- var start = this.getTick(i-1);
- var end = this.getTick(i);
+ // Index after the split.
+ getSplitIndex: function () {
+ return this._splitIndex;
+ },
- if (start === undefined || end === undefined || isNaN(start) || isNaN(end)) {
- return undefined;
- }
+ getStartIndex: function () {
+ return this._startIndex;
+ },
- return {
- index: i,
- start: start,
- end: end,
- size: end - start,
- pos: end,
- };
+ getEndIndex: function () {
+ return this._endIndex;
},
- // Returns true when the i-th gap has zero size.
- isZeroSize: function isZeroSize(i) {
- return this.getGap(i).size === 0;
+ getMinIndex: function () {
+ return this._hasSplits ? 0 : this._startIndex;
},
- // Runs the given callback function for each tick
- // The callback function receives two parameters: the tick index and the
- // (interpolated) tick position
- forEachTick: function forEachTick(callback) {
- for (var idx=this._minTickIdx; idx <= this._maxTickIdx; idx++) {
- if (callback(idx, this.getTick(idx)))
- break;
+ getMaxIndex: function () {
+ return this._endIndex;
+ },
+
+ getElementData: function (index) {
+ return this._elements[index];
+ },
+
+ getRowData: function (index) {
+ console.assert(!this._isCol, 'this is a column header instance!');
+ return this.getElementData(index);
+ },
+
+ getColData: function (index) {
+ console.assert(this._isCol, 'this is a row header instance!');
+ return this.getElementData(index);
+ },
+
+ getPreviousIndex: function (index) {
+
+ var prevIndex;
+ if (this._splitIndex && index === this._startIndex) {
+ prevIndex = this._splitIndex - 1;
}
+ else {
+ prevIndex = index - 1;
+ }
+
+ return prevIndex;
},
- // Runs the given callback function for each gap
- // The callback receives one parameter, in the same format as the return value
- // of getGap()
- forEachGap: function forEachGap(callback) {
- for (var idx=this._minTickIdx; idx < this._maxTickIdx; idx++) {
- if (callback(this.getGap(idx+1)))
- break;
+ getNextIndex: function (index) {
+
+ var nextIndex;
+ if (this._splitIndex && index === (this._splitIndex - 1)) {
+ nextIndex = this._startIndex;
+ }
+ else {
+ nextIndex = index + 1;
+ }
+
+ return nextIndex;
+ },
+
+ forEachElement: function (callback) {
+ var idx;
+ if (this._hasSplits) {
+ for (idx = 0; idx < this._splitIndex; ++idx) {
+ console.assert(this._elements[idx], 'forEachElement failed');
+ if (callback(this._elements[idx])) {
+ return;
+ }
+ }
+ }
+ for (idx = this._startIndex; idx <= this._endIndex; ++idx) {
+ console.assert(this._elements[idx], 'forEachElement failed');
+ if (callback(this._elements[idx])) {
+ return;
+ }
}
},
diff --git a/loleaflet/src/control/Control.RowHeader.js b/loleaflet/src/control/Control.RowHeader.js
index 820f2205b..2e55fc4b7 100644
--- a/loleaflet/src/control/Control.RowHeader.js
+++ b/loleaflet/src/control/Control.RowHeader.js
@@ -11,6 +11,7 @@ L.Control.RowHeader = L.Control.Header.extend({
onAdd: function (map) {
map.on('updatepermission', this._onUpdatePermission, this);
+ map.on('moveend zoomchanged sheetgeometrychanged splitposchanged', this._updateCanvas, this);
this._initialized = false;
},
@@ -152,6 +153,13 @@ L.Control.RowHeader = L.Control.Header.extend({
this._map.sendUnoCommand('.uno:ShowRow');
},
+ _updateCanvas: function () {
+ if (this._headerInfo) {
+ this._headerInfo.update();
+ this._redrawHeaders();
+ }
+ },
+
setScrollPosition: function (e) {
var position = -e.y;
this._position = Math.min(0, position);
@@ -179,7 +187,7 @@ L.Control.RowHeader = L.Control.Header.extend({
},
_onUpdateCurrentRow: function (e) {
- var y = e.curY;
+ var y = e.curY - 1; // 1-based to 0-based.
var h = this._twipsToPixels(e.height);
var slim = h <= 1;
this.updateCurrent(y, slim);
@@ -194,10 +202,10 @@ L.Control.RowHeader = L.Control.Header.extend({
return;
var ctx = this._canvasContext;
- var content = entry.index;
+ var content = entry.index + 1;
var startOrt = this._canvasWidth - this._headerWidth;
- var startPar = entry.pos - entry.size - this._startOffset;
- var endPar = entry.pos - this._startOffset;
+ var startPar = entry.pos - entry.size;
+ var endPar = entry.pos;
var height = endPar - startPar;
var width = this._headerWidth;
@@ -211,7 +219,6 @@ L.Control.RowHeader = L.Control.Header.extend({
ctx.save();
var scale = L.getDpiScaleFactor();
ctx.scale(scale, scale);
- ctx.translate(0, this._position + this._startOffset);
// background gradient
var selectionBackgroundGradient = null;
if (isHighlighted) {
@@ -268,14 +275,13 @@ L.Control.RowHeader = L.Control.Header.extend({
var level = group.level;
var startOrt = spacing + (headSize + spacing) * level;
- var startPar = group.startPos - this._startOffset;
+ var startPar = this._headerInfo.docToHeaderPos(group.startPos);
var height = group.endPos - group.startPos;
ctx.save();
var scale = L.getDpiScaleFactor();
ctx.scale(scale, scale);
- ctx.translate(0, this._position + this._startOffset);
// clip mask
ctx.beginPath();
ctx.rect(startOrt, startPar, headSize, height);
@@ -347,15 +353,15 @@ L.Control.RowHeader = L.Control.Header.extend({
var entry = this._mouseOverEntry;
if (index)
- entry = this._tickMap.getGap(index);
+ entry = this._headerInfo.getRowData(index);
if (!entry)
return;
var rect = this._canvas.getBoundingClientRect();
- var rowStart = entry.pos - entry.size + this._position;
- var rowEnd = entry.pos + this._position;
+ var rowStart = entry.pos - entry.size;
+ var rowEnd = entry.pos;
var left = rect.left;
var right = rect.right;
@@ -397,11 +403,12 @@ L.Control.RowHeader = L.Control.Header.extend({
}
var sheetGeometry = this._map._docLayer.sheetGeometry;
- var rowsGeometry = sheetGeometry ? sheetGeometry.getRowsGeometry() : undefined;
- // create data structure for row heights
- this._tickMap = new L.Control.Header.GapTickMap(this._map, rows, rowsGeometry);
- this._startOffset = this._tickMap.getStartOffset();
+ if (!this._headerInfo) {
+ // create data structure for row heights
+ this._headerInfo = new L.Control.Header.HeaderInfo(this._map, false /* isCol */);
+ this._map._rowHdr = this._headerInfo;
+ }
// setup conversion routine
this.converter = L.Util.bind(converter, context);
@@ -426,13 +433,7 @@ L.Control.RowHeader = L.Control.Header.extend({
this.resize(this._headerWidth);
}
- // Initial draw
- this._tickMap.forEachGap(function(gap) {
- this.drawHeaderEntry(gap, false);
- }.bind(this));
-
- // draw group controls
- this.drawOutline();
+ this._redrawHeaders();
this.mouseInit(canvas);
@@ -441,11 +442,21 @@ L.Control.RowHeader = L.Control.Header.extend({
}
},
+ _redrawHeaders: function () {
+ this._canvasContext.clearRect(0, 0, this._canvas.width, this._canvas.height);
+ this._headerInfo.forEachElement(function(elemData) {
+ this.drawHeaderEntry(elemData, false);
+ }.bind(this));
+
+ // draw group controls
+ this.drawOutline();
+ },
+
_selectRow: function(row, modifier) {
var command = {
Row: {
type: 'long',
- value: row - 1
+ value: row
},
Modifier: {
type: 'unsigned short',
@@ -505,11 +516,18 @@ L.Control.RowHeader = L.Control.Header.extend({
},
_getHorzLatLng: function (start, offset, e) {
- var limit = this._map.mouseEventToContainerPoint({clientX: start.x, clientY: start.y});
+ var size = this._map.getSize();
var drag = this._map.mouseEventToContainerPoint(e);
+ var entryStart = this._dragEntry.pos - this._dragEntry.size;
+ var ydocpos = this._headerInfo.headerToDocPos(Math.max(drag.y, entryStart));
+ var xmin = this._map.getPixelBounds().min.x;
+ var xmax = xmin + size.x;
+ if (this._headerInfo.hasSplits()) {
+ xmin = 0;
+ }
return [
- this._map.containerPointToLatLng(new L.Point(0, Math.max(limit.y, drag.y + offset.y))),
- this._map.containerPointToLatLng(new L.Point(this._map.getSize().x, Math.max(limit.y, drag.y + offset.y)))
+ this._map.unproject(new L.Point(xmin, ydocpos)),
+ this._map.unproject(new L.Point(xmax, ydocpos)),
];
},
@@ -539,8 +557,9 @@ L.Control.RowHeader = L.Control.Header.extend({
var height = clickedRow.size;
var row = clickedRow.index;
- if (this._tickMap.isZeroSize(clickedRow.index + 1)) {
- row += 1;
+ var nextRow = this._headerInfo.getNextIndex(clickedRow.index);
+ if (this._headerInfo.isZeroSize(nextRow)) {
+ row = nextRow;
height = 0;
}
@@ -552,7 +571,7 @@ L.Control.RowHeader = L.Control.Header.extend({
},
Row: {
type: 'long',
- value: row
+ value: row + 1 // core expects 1-based index.
}
};
@@ -574,7 +593,7 @@ L.Control.RowHeader = L.Control.Header.extend({
var command = {
Row: {
type: 'long',
- value: row - 1
+ value: row
},
Modifier: {
type: 'unsigned short',
diff --git a/loleaflet/src/layer/CalcGridLines.js b/loleaflet/src/layer/CalcGridLines.js
index 37492b606..fce802101 100644
--- a/loleaflet/src/layer/CalcGridLines.js
+++ b/loleaflet/src/layer/CalcGridLines.js
@@ -72,23 +72,17 @@ L.CalcGridLines = L.LayerGroup.extend({
// Redraw col/row lines whenever new information about them is available.
// One websocket message might have info about cols, rows, or both
onUpdate: function onUpdate(ev) {
- var ticks;
+ var headerInfo, pos;
- // Aux stuff to scale twips from the websocket message
- // into map coordinate units
+ // Aux stuff to convert css pixels to map coordinate units
var pixelToMapUnitRatio = this._map.options.crs.scale(this._map.getZoom());
- var colDataInEvent = ev.data && ev.data.columns && ev.data.columns.length;
- var rowDataInEvent = ev.data && ev.data.rows && ev.data.rows.length;
-
- if (colDataInEvent || ev.updatecolumns) {
- var columnsData = colDataInEvent ? ev.data.columns : undefined;
- var columnsGeometry = colDataInEvent ? undefined : this._map._docLayer.sheetGeometry.getColumnsGeometry();
- ticks = new L.Control.Header.GapTickMap(this._map, columnsData, columnsGeometry);
+ if (ev.updatecolumns) {
+ headerInfo = new L.Control.Header.HeaderInfo(this._map, true /* isCol */);
this._colLines.clearLayers();
- ticks.forEachTick(function(idx, pos) {
- pos /= pixelToMapUnitRatio;
+ headerInfo.forEachElement(function(columnData) {
+ pos = headerInfo.headerToDocPos(columnData.pos) / pixelToMapUnitRatio;
this._colLines.addLayer(
L.polyline([[[ L.Util.MIN_SAFE_INTEGER, pos ],[ L.Util.MAX_SAFE_INTEGER, pos ]]],
this.options
@@ -97,14 +91,12 @@ L.CalcGridLines = L.LayerGroup.extend({
}.bind(this));
}
- if (rowDataInEvent || ev.updaterows) {
- var rowsData = rowDataInEvent ? ev.data.rows : undefined;
- var rowsGeometry = rowDataInEvent ? undefined : this._map._docLayer.sheetGeometry.getRowsGeometry();
- ticks = new L.Control.Header.GapTickMap(this._map, rowsData, rowsGeometry);
+ if (ev.updaterows) {
+ headerInfo = new L.Control.Header.HeaderInfo(this._map, false /* isCol */);
this._rowLines.clearLayers();
- ticks.forEachTick(function(idx, pos) {
- pos /= pixelToMapUnitRatio;
+ headerInfo.forEachElement(function(rowData) {
+ pos = headerInfo.headerToDocPos(rowData.pos) / pixelToMapUnitRatio;
this._rowLines.addLayer(
// Note that y-coordinates are inverted: Leaflet's CRS.Simple assumes
// down = negative latlngs, whereas loolkit assumes down = positive twips
diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js b/loleaflet/src/layer/tile/CalcTileLayer.js
index 4ff7b51ba..9d711b48f 100644
--- a/loleaflet/src/layer/tile/CalcTileLayer.js
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -449,6 +449,7 @@ L.CalcTileLayer = L.TileLayer.extend({
}
this._restrictDocumentSize();
this._replayPrintTwipsMsgs();
+ this._map.fire('zoomchanged');
this.refreshViewData();
this._map._socket.sendMessage('commandvalues command=.uno:ViewAnnotationsPosition');
},
@@ -719,6 +720,8 @@ L.CalcTileLayer = L.TileLayer.extend({
this._pixelsToTwips(this._map.getSize()));
this._updateHeadersGridLines(undefined, true /* updateCols */,
true /* updateRows */);
+
+ this._map.fire('sheetgeometrychanged');
},
_onCommandValuesMsg: function (textMsg) {
More information about the Libreoffice-commits
mailing list