tdf#111535 Add First-line indent, paragraph indent, and tab spaces/Online part.

Change-Id: I7b0b6e205aa9af40b8331044b99c44df0bd4cced
Reviewed-on: https://gerrit.libreoffice.org/c/online/+/93250
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/loleaflet/css/leaflet.css b/loleaflet/css/leaflet.css
index f8f5eb9..ec6a3d3 100644
--- a/loleaflet/css/leaflet.css
+++ b/loleaflet/css/leaflet.css
@@ -842,6 +842,39 @@ input.clipboard {
	position: absolute;
	}

.loleaflet-ruler-indentation-marker-down {
	width: 17px;
	height: 11px;
	background: url(images/indentation_marker_down.svg);
	position: absolute;
	left: 0;
	top: 0;
	z-index: 12;
	background-repeat: no-repeat;
}

.loleaflet-ruler-indentation-marker-up {
	width: 17px;
	height: 11px;
	background: url(images/indentation_marker_up.svg);
	position: absolute;
	left: 0;
	bottom: 0;
	z-index: 12;
	background-repeat: no-repeat;
}

.loleaflet-ruler-indentation-marker-center{
	position: fixed;
	display: none;
	height: 100vh;
	top: 0;
	bottom: 0;
	left: 0;
	border-right: 2px dotted grey;
	z-index: 12;
}

.loleaflet-ruler-marginwrapper {
	height: 100%;
	position: absolute;
diff --git a/loleaflet/images/indentation_marker_down.svg b/loleaflet/images/indentation_marker_down.svg
new file mode 100644
index 0000000..41e73bb
--- /dev/null
+++ b/loleaflet/images/indentation_marker_down.svg
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 140 110" >
    <polygon points="10,10 130,10 130,50 70,100 10,50" />
</svg>
\ No newline at end of file
diff --git a/loleaflet/images/indentation_marker_up.svg b/loleaflet/images/indentation_marker_up.svg
new file mode 100644
index 0000000..88f90c0
--- /dev/null
+++ b/loleaflet/images/indentation_marker_up.svg
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 140 110" >
    <polygon points="10,100 130,100 130,60 70,10 10,60" />
</svg>
\ No newline at end of file
diff --git a/loleaflet/src/control/Ruler.js b/loleaflet/src/control/Ruler.js
index 3c849aa..98b6537 100644
--- a/loleaflet/src/control/Ruler.js
+++ b/loleaflet/src/control/Ruler.js
@@ -64,6 +64,89 @@ L.Control.Ruler = L.Control.extend({
		}
	},

	_initiateIndentationMarkers: function() {
		// First line indentation..
		this._firstLineMarker = document.createElement('div');
		this._firstLineMarker.id = 'lo-fline-marker';
		this._firstLineMarker.classList.add('loleaflet-ruler-indentation-marker-down');
		this._rFace.appendChild(this._firstLineMarker);

		// Paragraph indentation..
		this._pStartMarker = document.createElement('div');
		this._pStartMarker.id = 'lo-pstart-marker';
		this._pStartMarker.classList.add('loleaflet-ruler-indentation-marker-up');
		this._rFace.appendChild(this._pStartMarker);

		// Paragraph end..
		this._pEndMarker = document.createElement('div');
		this._pEndMarker.id = 'lo-pend-marker';
		this._pEndMarker.classList.add('loleaflet-ruler-indentation-marker-up');
		this._rFace.appendChild(this._pEndMarker);

		// While one of the markers is being dragged, a vertical line should be visible in order to indicate the new position of the marker..
		this._markerVerticalLine = L.DomUtil.create('div', 'loleaflet-ruler-indentation-marker-center');
		this._rFace.appendChild(this._markerVerticalLine);

		var self = this;

		// Now we have indentation markers. Next we should bind drag initializers to them..
		// We will use 3 hammers. 1 hammer is not usable for this case.
		if (L.Browser.touch) {
			// Hammer for first line indentation..
			this._firstLineHammer = new Hammer(this._firstLineMarker);
			this._firstLineHammer.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
			this._firstLineHammer.get('press').set({
				time: 500
			});
			this._firstLineHammer.on('panstart', function (event) {
				self._initiateIndentationDrag(event);
			});
			this._firstLineHammer.on('panmove', function (event) {
				self._moveIndentation(event);
			});
			this._firstLineHammer.on('panend', function (event) {
				self._moveIndentationEnd(event);
			});

			// Hammer for paragraph start indentation..
			this._pStartHammer = new Hammer(this._pStartMarker);
			this._pStartHammer.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
			this._pStartHammer.get('press').set({
				time: 500
			});
			this._pStartHammer.on('panstart', function (event) {
				self._initiateIndentationDrag(event);
			});
			this._pStartHammer.on('panmove', function (event) {
				self._moveIndentation(event);
			});
			this._pStartHammer.on('panend', function (event) {
				self._moveIndentationEnd(event);
			});

			// Hammer for paragraph end indentation..
			this._pEndHammer = new Hammer(this._pEndMarker);
			this._pEndHammer.add(new Hammer.Pan({ threshold: 0, pointers: 0 }));
			this._pEndHammer.get('press').set({
				time: 500
			});
			this._pEndHammer.on('panstart', function (event) {
				self._initiateIndentationDrag(event);
			});
			this._pEndHammer.on('panmove', function (event) {
				self._moveIndentation(event);
			});
			this._pEndHammer.on('panend', function (event) {
				self._moveIndentationEnd(event);
			});
		}
		else {
			L.DomEvent.on(this._firstLineMarker, 'mousedown', this._initiateIndentationDrag, this);
			L.DomEvent.on(this._pStartMarker, 'mousedown', this._initiateIndentationDrag, this);
			L.DomEvent.on(this._pEndMarker, 'mousedown', this._initiateIndentationDrag, this);
		}
	},

	_initLayout: function() {
		this._rWrapper = L.DomUtil.create('div', 'loleaflet-ruler leaflet-bar leaflet-control leaflet-control-custom');
		this._rFace = L.DomUtil.create('div', 'loleaflet-ruler-face', this._rWrapper);
@@ -97,12 +180,15 @@ L.Control.Ruler = L.Control.extend({
				self._onTabstopContainerLongPress(event);
			});
		}

		this._initiateIndentationMarkers();

		return this._rWrapper;
	},

	_updateOptions: function(obj) {
		// console.log('===> _updateOptions');
		// Note that the values for margin1, margin2, and leftOffset are not in any sane
		// Note that the values for margin1, margin2 and leftOffset are not in any sane
		// units. See the comment in SwCommentRuler::CreateJsonNotification(). The values
		// are pixels for some virtual device in core, not related to the actual device this
		// is running on at all, passed through convertTwipToMm100(), i.e. multiplied by
@@ -121,6 +207,42 @@ L.Control.Ruler = L.Control.extend({
		this._updateBreakPoints();
	},

	_updateParagraphIndentations: function() {
		var items = this._map['stateChangeHandler'];
		var state = items.getItemValue('.uno:LeftRightParaMargin');

		this.options.firstLineIndent = parseFloat(state.firstline.replace(',', '.'));
		this.options.leftParagraphIndent = parseFloat(state.left.replace(',', '.'));
		this.options.rightParagraphIndent = parseFloat(state.right.replace(',', '.'));
		this.options.indentUnit = state.unit;

		var pxPerMm100 = this._map._docLayer._docPixelSize.x / (this._map._docLayer._docWidthTwips * 2540/1440);

		// Conversion to mm100.
		if (this.options.indentUnit === 'inch') {
			this.options.firstLineIndent = (this.options.firstLineIndent) * 2540;
			this.options.leftParagraphIndent = (this.options.leftParagraphIndent) * 2540;
			this.options.rightParagraphIndent = (this.options.rightParagraphIndent) * 2540;
		}

		this.options.firstLineIndent *= pxPerMm100;
		this.options.leftParagraphIndent *= pxPerMm100;
		this.options.rightParagraphIndent *= pxPerMm100;

		// rTSContainer is the reference element.
		var pStartPosition = this._rTSContainer.getBoundingClientRect().left + this.options.leftParagraphIndent;
		var fLinePosition = pStartPosition + this.options.firstLineIndent;
		var pEndPosition = this._rTSContainer.getBoundingClientRect().right - this.options.rightParagraphIndent;

		// We calculated the positions. Now we should move them to left in order to make their sharp edge point to the right direction..
		var halfWidth = (this._firstLineMarker.getBoundingClientRect().right - this._firstLineMarker.getBoundingClientRect().left) * 0.5;
		this._firstLineMarker.style.left = (fLinePosition - halfWidth) + 'px';
		this._pStartMarker.style.left = (pStartPosition - halfWidth) + 'px';
		this._pEndMarker.style.left = (pEndPosition - halfWidth) + 'px';

		this._markerVerticalLine.style.top = this._rTSContainer.getBoundingClientRect().bottom + 'px';
	},

	_updateTabStops: function(obj) {
		this.options.tabs = [];
		var jsonTabstops = obj['tabstops'];
@@ -141,6 +263,8 @@ L.Control.Ruler = L.Control.extend({
		if (this.options.margin1 == null || this.options.margin2 == null)
			return;

		this._updateParagraphIndentations();

		if (this._map._docLayer._annotations._items.length === 0
		|| this._map._docLayer._annotations._items.length
		=== this._map._docLayer._annotations._hiddenItems
@@ -293,6 +417,97 @@ L.Control.Ruler = L.Control.extend({
		var rulerOffset = mapPaneXTranslate + firstTileXTranslate + tileContainerXTranslate + (this.options.tileMargin * scale);

		this._rFace.style.marginLeft = rulerOffset + 'px';

		this.rulerOffset = rulerOffset; // Needed on different parts too..
	},

	_moveIndentation: function(e) {
		if (e.type === 'panmove') {
			e.clientX = e.center.x;
		}

		var element = document.getElementById(this._indentationElementId);

		// User is moving the cursor / their finger on the screen and we are moving the marker.
		var newLeft = parseInt(element.style.left.replace('px', '')) + e.clientX - this._lastposition;
		element.style.left = String(newLeft) + 'px';
		this._lastposition = e.clientX;
		// halfWidth..
		var halfWidth = (element.getBoundingClientRect().right - element.getBoundingClientRect().left) * 0.5;
		this._markerVerticalLine.style.left = String(newLeft + halfWidth) + 'px';
	},

	_moveIndentationEnd: function(e) {
		this._map.rulerActive = false;

		if (e.type !== 'panend') { // The screen may support touch and click at the same time, so we do not use L.Browser.touch while checking the event type in order to support both.
			L.DomEvent.off(this._rFace, 'mousemove', this._moveIndentation, this);
			L.DomEvent.off(this._map, 'mouseup', this._moveIndentationEnd, this);
		}

		var unoObj = {}, indentType = '';

		// Calculation step..
		// The new coordinate of element subject to indentation is sent as a percentage of the page width..
		// We need to calculate the percentage. Left margin (nullOffset) is not being added to the indentation (on the core part)..
		// We can use TabStopContainer's position as the reference point, as they share the same reference point..
		var element = document.getElementById(this._indentationElementId);

		var leftValue;
		// The halfWidth of the shape..
		var halfWidth = (element.getBoundingClientRect().right - element.getBoundingClientRect().left) * 0.5;

		// We need the pageWidth in pixels, so we can not use "this.options.pageWidth" here, since that's in mm..
		var pageWidth = parseFloat(this._rFace.style.width.replace('px', ''));

		if (element.id === 'lo-fline-marker') {
			indentType = 'FirstLineIndent';
			// FirstLine indentation is always positioned according to the left indent..
			// We don't need to add halfWidth here..
			leftValue = (parseFloat(this._firstLineMarker.style.left.replace('px', '')) - parseFloat(this._pStartMarker.style.left.replace('px', '')));
		}
		else if (element.id === 'lo-pstart-marker') {
			indentType = 'LeftParaIndent';
			leftValue = element.getBoundingClientRect().left - this._rTSContainer.getBoundingClientRect().left + halfWidth;
		}
		else if (element.id === 'lo-pend-marker') {
			indentType = 'RightParaIndent';
			// Right marker is positioned from right, this is rightValue..
			leftValue = this._rTSContainer.getBoundingClientRect().right - element.getBoundingClientRect().right + halfWidth;
		}

		leftValue = leftValue / pageWidth; // Now it's a percentage..

		if (indentType !== '') {
			unoObj[indentType] = {};
			unoObj[indentType]['type'] = 'string';
			unoObj[indentType]['value'] = leftValue;
			this._map._socket.sendMessage('uno .uno:ParagraphChangeState ' + JSON.stringify(unoObj));
		}

		this._indentationElementId = '';
		this._markerVerticalLine.style.display = 'none';
	},

	_initiateIndentationDrag: function(e) {
		if (window.ThisIsTheiOSApp && this._map._permission !== 'edit')
			return;

		this._map.rulerActive = true;

		this._indentationElementId = e.target.id.trim() === '' ? e.target.parentNode.id: e.target.id;

		if (e.type !== 'panstart') {
			L.DomEvent.on(this._rFace, 'mousemove', this._moveIndentation, this);
			L.DomEvent.on(this._map, 'mouseup', this._moveIndentationEnd, this);
		}
		else {
			e.clientX = e.center.x;
		}

		this._initialposition = this._lastposition = e.clientX;
		this._markerVerticalLine.style.display = 'block';
		this._markerVerticalLine.style.left = this._lastposition + 'px';
	},

	_initiateDrag: function(e) {