collabora-online/browser/src/canvas/sections/ContentControlSection.ts

384 lines
12 KiB
TypeScript

/*
* Copyright the Collabora Online contributors.
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
declare var L: any;
declare var app: any;
namespace cool {
export class ContentControlSection extends CanvasSectionObject {
map: any;
public onInitialize(): void {
this.sectionProperties.polyAttri = {
stroke: true,
fill: false,
pointerEvents: 'none',
color: 'black',
weight: 1,
};
this.sectionProperties.dropdownButton = L.marker(new L.LatLng(0, 0), {
icon: L.divIcon({
className: 'writer-drop-down-marker',
iconSize: null
}),
interactive: true
});
this.onClickDropdown = this.onClickDropdown.bind(this);
this.sectionProperties.dropdownButton.addEventListener('click', this.onClickDropdown, false);
this.map.on('dropdownmarkertapped', this.onClickDropdown, this);
this.map.on('darkmodechanged', this.changeBorderStyle, this);
var container = L.DomUtil.createWithId('div', 'datepicker');
container.style.zIndex = '12';
container.style.position = 'absolute';
document.getElementById('document-container').appendChild(container);
this.sectionProperties.datePicker = false;
this.sectionProperties.picturePicker = false;
}
constructor() {
super({
processingOrder: L.CSections.ContentControl.processingOrder,
drawingOrder: L.CSections.ContentControl.drawingOrder,
zIndex: L.CSections.ContentControl.zIndex,
name: L.CSections.ContentControl.name,
interactable: false,
sectionProperties: {},
position: [0, 0],
size: [],
expand: '',
anchor: [],
});
this.myTopLeft = [0, 0];
this.documentObject = true;
this.map = L.Map.THIS;
this.sectionProperties.json = null;
this.sectionProperties.datePicker = null;
this.sectionProperties.picturePicker = null;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public drawContentControl(json: any) {
this.sectionProperties.json = json;
this.sectionProperties.datePicker = false;
this.sectionProperties.picturePicker = false;
if (this.sectionProperties.dropdownButton)
this.map.removeLayer(this.sectionProperties.dropdownButton);
if (json.date) {
this.sectionProperties.datePicker = true;
$.datepicker.setDefaults($.datepicker.regional[(<any>window).langParamLocale.language]);
$('#datepicker').datepicker({
onSelect: function (date: any, datepicker: any) {
if (date != '') {
app.socket.sendMessage('contentcontrolevent type=date selected=' + date);
}
}
});
$('#datepicker').hide();
} else {
this.sectionProperties.datePicker = false;
$('#datepicker').datepicker('destroy');
}
if (json.action === 'show') {
this.drawPolygon();
} else if (json.action === 'hide') {
if (this.sectionProperties.frame)
this.sectionProperties.frame.setPointSet(new CPointSet());
} else if (json.action === 'change-picture') {
this.sectionProperties.picturePicker = true;
if (!this.map.wopi.EnableInsertRemoteImage)
L.DomUtil.get('insertgraphic').click();
else
this.map.fire('postMessage', {msgId: 'UI_InsertGraphic'});
}
this.setPositionAndSize();
app.sectionContainer.requestReDraw();
}
private setPositionAndSize (): void {
if (!this.sectionProperties.json || !this.sectionProperties.json.rectangles)
return;
var rectangles: Array<number>[] = this.getRectangles(this.sectionProperties.json.rectangles);
var xMin: number = Infinity, yMin: number = Infinity, xMax: number = 0, yMax: number = 0;
for (var i = 0; i < rectangles.length; i++) {
if (rectangles[i][0] < xMin)
xMin = rectangles[i][0];
if (rectangles[i][1] < yMin)
yMin = rectangles[i][1];
if (rectangles[i][0] + rectangles[i][2] > xMax)
xMax = rectangles[i][0] + rectangles[i][2];
if (rectangles[i][1] + rectangles[i][3] > yMax)
yMax = rectangles[i][1] + rectangles[i][3];
}
// Rectangles are in twips. Convert them to core pixels.
var ratio: number = (app.tile.size.pixels[0] / app.tile.size.twips[0]);
xMin = Math.round(xMin * ratio);
yMin = Math.round(yMin * ratio);
xMax = Math.round(xMax * ratio);
yMax = Math.round(yMax * ratio);
this.setPosition(xMin, yMin); // This function is added by section container.
this.size = [xMax - xMin, yMax - yMin];
if (this.size[0] < 5)
this.size[0] = 5;
}
public onResize (): void {
this.setPositionAndSize();
}
public drawPolygon(): void {
var rectArray = cool.Bounds.parseArray(this.sectionProperties.json.rectangles);
var rectangles = rectArray.map(function (rect: cool.Bounds) {
return rect.getPointArray();
});
var docLayer = this.map._docLayer;
this.sectionProperties.pointSet = CPolyUtil.rectanglesToPointSet(rectangles,
function (twipsPoint) {
var corePxPt = docLayer._twipsToCorePixels(twipsPoint);
corePxPt.round();
return corePxPt;
});
if (!this.sectionProperties.frame) {
this.sectionProperties.frame = new CPolygon(this.sectionProperties.pointSet, this.sectionProperties.polyAttri);
this.changeBorderStyle();
this.map._docLayer._canvasOverlay.initPath(this.sectionProperties.frame);
}
this.sectionProperties.frame.setPointSet(this.sectionProperties.pointSet);
}
public onDraw(): void {
if (!this.sectionProperties.json)
return;
var text:string = this.sectionProperties.json.alias;
if (text) {
var rectangles: Array<number>[] = this.getRectangles(this.sectionProperties.json.rectangles);
var ratio: number = (app.tile.size.pixels[0] / app.tile.size.twips[0]);
var x: number = rectangles[rectangles.length-1][0] * ratio;
var y: number = rectangles[rectangles.length-1][1] * ratio;
// fixed height for alias tag
var h: number = 20;
var startX: number = x - this.position[0] + 5;
var startY: number = y - this.position[1];
var padding: number = 10;
var fontStyle = getComputedStyle(document.body).getPropertyValue('--docs-font').split(',')[0].replace(/'/g, '');
var fontSize = getComputedStyle(document.body).getPropertyValue('--default-font-size');
var font = fontSize + ' ' + fontStyle;
var textWidth: number = L.Util.getTextWidth(text, font) + padding;
// draw rectangle with backgroundcolor
this.context.beginPath();
this.context.fillStyle = '#E6FFFF';
this.context.font = font;
this.context.fillRect(startX, startY - h, textWidth, h);
// add text to the rectangle
this.context.textAlign = 'center';
this.context.textBaseline = 'middle';
this.context.fillStyle = '#026296';
this.context.fillText(text, startX + textWidth / 2, startY - h / 2);
// draw borders around the rectangle
this.context.strokeStyle = '#026296';
this.context.lineWidth = app.dpiScale;
this.context.strokeRect(startX - 0.5, startY - h - 0.5, textWidth, h);
}
if (this.sectionProperties.json.items || this.sectionProperties.datePicker) {
this.addDropDownBtn();
}
}
public onNewDocumentTopLeft (): void {
this.setPositionAndSize();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
private callback(objectType: any , eventType: any, object: any, data: any, builder: any): void {
var fireEvent: string = 'jsdialog';
if ((<any>window).mode.isMobile()) {
fireEvent = 'mobilewizard';
}
var closeDropdownJson = {
'jsontype': 'dialog',
'type': 'modalpopup',
'action': 'close',
'id': builder.windowId,
};
if (eventType === 'close') {
this.map.fire(fireEvent, {data: closeDropdownJson, callback: undefined});
} else if (eventType === 'select') {
app.socket.sendMessage('contentcontrolevent type=drop-down selected='+ data);
this.map.fire(fireEvent, {data: closeDropdownJson, callback: undefined});
}
}
private addDropDownBtn(): void {
var matches = this.sectionProperties.json.rectangles.match(/\d+/g);
//consider first rectangle to position dropdownbutton
var rectangle = [parseInt(matches[0]), parseInt(matches[1]), parseInt(matches[2]), parseInt(matches[3])];
var topLeftTwips = new cool.Point(rectangle[0], rectangle[1]);
var offset = new cool.Point(rectangle[2], rectangle[3]);
var bottomRightTwips = topLeftTwips.add(offset);
var buttonAreaTwips = [topLeftTwips, bottomRightTwips];
var frameArea = new cool.Bounds(
this.map._docLayer._twipsToPixels(topLeftTwips),
this.map._docLayer._twipsToPixels(bottomRightTwips));
var size = frameArea.getSize();
var origin = this.map.getPixelOrigin();
var panePos = this.map._getMapPanePos();
this.sectionProperties.framePos = new cool.Point(Math.round(frameArea.min.x + panePos.x - origin.x), Math.round(frameArea.min.y + panePos.y - origin.y));
this.sectionProperties.frameWidth = Math.round(size.x);
this.sectionProperties.frameHeight = Math.round(size.y);
var icon = L.divIcon({
className: 'writer-drop-down-marker',
iconSize: [this.sectionProperties.frameHeight, this.sectionProperties.frameHeight],
iconAnchor: [0, 0]
});
this.sectionProperties.dropdownButton.setIcon(icon);
// Then convert to unit which can be used on the layer.
var buttonAreaLatLng = new L.LatLngBounds(
this.map._docLayer._twipsToLatLng(buttonAreaTwips[0], this.map.getZoom()),
this.map._docLayer._twipsToLatLng(buttonAreaTwips[1], this.map.getZoom()));
var pos = buttonAreaLatLng.getNorthEast();
this.sectionProperties.dropdownButton.setLatLng(pos);
this.map.addLayer(this.sectionProperties.dropdownButton);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
private openDropdownJson(): any {
if (!this.sectionProperties.json.items)
return;
var json: any = {
'children': [
{
'id': 'container-dropdown',
'type': 'container',
'text': '',
'enabled': true,
'children': [
{
'id': 'contentControlList',
'type': 'treelistbox',
'text': '',
'enabled': true,
'singleclickactivate': true,
}
],
'vertical': true
}
],
'jsontype': 'dialog',
'type': 'modalpopup',
'cancellable': true,
'popupParent': '_POPOVER_',
'clickToClose': '_POPOVER_',
'id': 'contentControlModalpopup'
};
var entries = [];
var items = this.sectionProperties.json.items;
//add entries
for (var i in items) {
var entry = {
'text' : items[i],
'columns': [
{
'text': items[i]
}
],
'row': i.toString()
};
entries.push(entry);
}
json.children[0].children[0].entries = entries;
//add position
json.posx = this.sectionProperties.framePos.x + this.sectionProperties.frameWidth;
json.posy = this.sectionProperties.framePos.y + this.sectionProperties.frameHeight;
return json;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
public onClickDropdown(event: any): void {
if (this.sectionProperties.datePicker) {
this.showDatePicker();
} else if (this.sectionProperties.json.items) {
var fireEvent: string = 'jsdialog';
if ((<any>window).mode.isMobile()) {
fireEvent = 'mobilewizard';
}
this.map.fire(fireEvent, {data: this.openDropdownJson(), callback: this.callback});
}
L.DomEvent.stopPropagation(event);
}
private showDatePicker(): void {
if ($('#datepicker').is(':visible')) {
$('#datepicker').hide();
} else {
var datePicker = document.getElementById('datepicker');
datePicker.style.left = this.sectionProperties.framePos.x + this.sectionProperties.frameWidth + 'px';
datePicker.style.top = this.sectionProperties.framePos.y + this.sectionProperties.frameHeight + 'px';
$('#datepicker').show();
}
}
private getRectangles(rect: string): Array<number>[] {
var rectangles: Array<number>[] = [];
//convert string to number coordinates
var matches = rect.match(/\d+/g);
if (matches !== null) {
for (var i: number = 0; i < matches.length; i += 4) {
rectangles.push([parseInt(matches[i]), parseInt(matches[i + 1]), parseInt(matches[i + 2]), parseInt(matches[i + 3])]);
}
}
return rectangles;
}
private changeBorderStyle(): void {
var borderColor = this.map.uiManager.getDarkModeState() ? 'white' : 'black';
this.sectionProperties.frame.color = borderColor;
}
}
}
app.definitions.ContentControlSection = cool.ContentControlSection;