UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

155 lines (149 loc) 7.7 kB
import { ClipboardCommand } from "./ClipboardCommand"; import { SimpleCommandState } from "../CommandStates"; import { Importer } from "../../ImportAndExport/Importer"; import { Shape } from "../../Model/Shapes/Shape"; import { ImportShapeHistoryItem } from "../../History/Common/ImportShapeHistoryItem"; import { Connector } from "../../Model/Connectors/Connector"; import { ImportConnectorHistoryItem } from "../../History/Common/ImportConnectorHistoryItem"; import { ModelUtils } from "../../Model/ModelUtils"; import { SetSelectionHistoryItem } from "../../History/Common/SetSelectionHistoryItem"; import { DiagramItem } from "../../Model/DiagramItem"; import { Point } from "@devexpress/utils/lib/geometry/point"; import { UnitConverter } from "@devexpress/utils/lib/class/unit-converter"; import { DiagramModel } from "../../Model/Model"; export abstract class PasteSelectionCommandBase extends ClipboardCommand { static readonly positionOffset = UnitConverter.pixelsToTwips(10); isEnabled(): boolean { return super.isEnabled() && (this.isPasteSupportedByBrowser() || ClipboardCommand.clipboardData !== undefined); } isVisible(): boolean { return this.isPasteSupportedByBrowser() || ClipboardCommand.clipboardData !== undefined; } parseClipboardData(data: string): DiagramItem[] { let items: DiagramItem[] = []; const importer = new Importer(this.control.shapeDescriptionManager, data); items = importer.importItems(this.control.model); let offset = this.getEventPositionOffset(items, this.control.contextMenuPosition); offset = this.getCorrectedOffsetByModel(items, offset); for(let i = 0; i < items.length; i++) { const item = items[i]; if(item instanceof Shape) item.position.offsetByPoint(offset); else if(item instanceof Connector) item.points.forEach(p => p.offsetByPoint(offset)); } return items; } protected abstract getEventPositionOffset(items: DiagramItem[], evtPosition?: Point): Point; protected getCorrectedOffsetByModel(items: DiagramItem[], baseOffset: Point): Point { const { topLeftItem } = items.reduce((acc, item) => { const x = item instanceof Shape ? item.position.x : item instanceof Connector ? item.getMinX() : Number.MAX_VALUE; const y = item instanceof Shape ? item.position.y : item instanceof Connector ? item.getMinY() : Number.MAX_VALUE; if(y < acc.y || (y === acc.y && x < acc.x)) { acc.topLeftItem = item; acc.x = x; acc.y = y; } return acc; }, { topLeftItem: items[0], x: Number.MAX_VALUE, y: Number.MAX_VALUE }); if(topLeftItem instanceof Shape) { const newPoint = this.getShapeCorrectedPosition(this.control.model, topLeftItem, baseOffset); return new Point(newPoint.x - topLeftItem.position.x, newPoint.y - topLeftItem.position.y); } else if(topLeftItem instanceof Connector) { const newPoints = this.getConnectorCorrectedPoints(this.control.model, topLeftItem, baseOffset); return new Point(topLeftItem.points[0].x - newPoints[0].x, topLeftItem.points[0].y - newPoints[0].y); } } executeCore(state: SimpleCommandState, parameter?: string): boolean { let ret = true; if(parameter) this.performPaste(parameter); else this.getClipboardData(data => { ret = this.execute(data); }); return ret; } addItemForSortingRecursive(itemsHashtable: {[key: string]: number}, item: DiagramItem): number { if(itemsHashtable[item.key]) return itemsHashtable[item.key]; if(item instanceof Connector) { if(item.endItem) itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, item.endItem) - 0.5; else if(item.beginItem) itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, item.beginItem) + 0.5; else itemsHashtable[item.key] = -1; return itemsHashtable[item.key]; } if(item.attachedConnectors.length === 0) return itemsHashtable[item.key] = 0; else for(let i = 0; i < item.attachedConnectors.length; i++) { const beginItem = item.attachedConnectors[i].beginItem; if(item.attachedConnectors[i].endItem === item && beginItem && beginItem !== item.attachedConnectors[i].endItem) return itemsHashtable[item.key] = this.addItemForSortingRecursive(itemsHashtable, beginItem) + 1; else return itemsHashtable[item.key] = 0; } } getSortedPasteItems(items: DiagramItem[]): DiagramItem[] { const sortedItems: DiagramItem[] = []; const connectors = []; const itemsHashtable = {}; for(let i = 0; i < items.length; i++) { const item = items[i]; if(item instanceof Shape) sortedItems.push(item); else if(item instanceof Connector) { connectors.push(item); this.addItemForSortingRecursive(itemsHashtable, item); } } connectors.sort((a, b) => itemsHashtable[b.key] - itemsHashtable[a.key]); return sortedItems.concat(connectors); } performPaste(data: string): void { this.control.beginUpdateCanvas(); this.control.history.beginTransaction(); const ids = []; let items = this.parseClipboardData(data); items = this.getSortedPasteItems(items); for(let i = 0; i < items.length; i++) { const item = items[i]; if(item instanceof Shape) this.control.history.addAndRedo(new ImportShapeHistoryItem(item)); else if(item instanceof Connector) this.control.history.addAndRedo(new ImportConnectorHistoryItem(item)); const containerKey = item.container && item.container.key; if(!containerKey || ids.indexOf(containerKey) === -1) ids.push(item.key); } ModelUtils.tryUpdateModelRectangle(this.control.history); this.control.history.addAndRedo(new SetSelectionHistoryItem(this.control.selection, ids)); this.control.history.endTransaction(); this.control.endUpdateCanvas(); this.control.barManager.updateItemsState(); } protected getShapeCorrectedPosition(model: DiagramModel, shape: Shape, initOffset: Point): Point { const position = shape.position.clone().offsetByPoint(initOffset); while(model.findShapeAtPosition(position)) position.offset(PasteSelectionCommandBase.positionOffset, PasteSelectionCommandBase.positionOffset); return position; } protected getConnectorCorrectedPoints(model: DiagramModel, connector: Connector, initOffset: Point): Point[] { const points = connector.points.map(p => p.clone().offsetByPoint(initOffset)); while(model.findConnectorAtPoints(points)) points.forEach(pt => { pt.x += PasteSelectionCommandBase.positionOffset; pt.y += PasteSelectionCommandBase.positionOffset; }); return points; } protected get isPermissionsRequired(): boolean { return true; } }