UNPKG

devexpress-diagram

Version:

DevExpress Diagram Control

173 lines (165 loc) 9.31 kB
import { Vector } from "@devexpress/utils/lib/geometry/vector"; import { ChangeConnectorPointsHistoryItem } from "../../History/Common/ChangeConnectorPointsHistoryItem"; import { History } from "../../History/History"; import { Connector } from "../Connectors/Connector"; import { ConnectorRenderPointsContext } from "../Connectors/Routing/ConnectorRenderPointsContext"; import { DiagramItem } from "../DiagramItem"; import { ModelUtils } from "../ModelUtils"; import { DiagramModel } from "../Model"; import { Point } from "@devexpress/utils/lib/geometry/point"; import { Shape } from "../Shapes/Shape"; import { ConnectorRenderPoint } from "../Connectors/ConnectorRenderPoint"; import { IPermissionsProvider } from "../Permissions/PermissionsProvider"; import { DiagramModelOperation } from "../../ModelOperationSettings"; export class SelectionDragHelper { draggingShapes: DraggingShape[] = []; draggingConnectors: DraggingConnector[] = []; selectedItems : { [key: string] : DiagramItem } = {}; draggingConnectorsIndexByKey: { [key: string]: number } = {}; modelConnectorsWithoutBeginItemInfo: { connector: Connector, point: Point }[]; modelConnectorsWithoutEndItemInfo: { connector: Connector, point: Point }[]; constructor(private history: History, private model: DiagramModel, private permissionsProvider: IPermissionsProvider, public startPoint: Point, selectedItems: DiagramItem[]) { selectedItems.forEach(i => this.selectedItems[i.key] = i); } initDraggingShapes(shapes: Shape[], shouldClone: boolean): void { this.draggingShapes = shapes.map(s => new DraggingShape(s)); if(!shouldClone) this.draggingShapes.forEach(draggingShape => this.permissionsProvider.addInteractingItem(draggingShape.shape, DiagramModelOperation.MoveShape)); } initDraggingConnectors(connectors: Connector[], shouldClone: boolean): void { this.draggingConnectors = []; this.draggingConnectorsIndexByKey = {}; connectors.forEach(c => this.registerConnector(c)); if(!shouldClone) this.draggingShapes.forEach(x => { const attachedConnectors = x.shape.attachedConnectors; if(attachedConnectors) attachedConnectors.forEach(c => { if(!this.containsDraggingConnectorByKey(c.key)) this.registerConnector(c); }); }); this.modelConnectorsWithoutBeginItemInfo = this.createModelConnectorsWithoutBeginItemInfo(); this.modelConnectorsWithoutEndItemInfo = this.createModelConnectorsWithoutEndItemInfo(); } move(shouldClone: boolean, getMovePoint: (point: Point) => Point, resetTargetCallback: () => void, updateTargetCallback: (shape: Shape, connectionPointIndex: number) => void): void { if(this.draggingShapes.length) { const selectedShapes = this.draggingShapes.map(ds => ds.shape); this.draggingShapes.forEach(ds => { let shape = ds.shape; while(shape.container) { if(selectedShapes.indexOf(shape.container) !== -1) return false; shape = shape.container; } this.moveShape(ds, getMovePoint, resetTargetCallback, updateTargetCallback); }); const firstDraggingShape = this.draggingShapes[0]; const offset = Vector.fromPoints(firstDraggingShape.startPosition.clone(), firstDraggingShape.shape.position.clone()); if(offset.x || offset.y) this.draggingConnectors.forEach(dc => this.moveConnectorCore(dc.connector, dc.startPoints, dc.startRenderContext, offset, shouldClone)); } else this.draggingConnectors.forEach(x => this.moveConnector(x, shouldClone, getMovePoint)); } containsDraggingConnectorByKey(key: string): boolean { return this.draggingConnectorsIndexByKey[key] !== undefined; } onTryUpdateModelSize(offsetLeft: number, offsetTop: number): void { this.modelConnectorsWithoutBeginItemInfo.forEach(pi => { pi.point.x += offsetLeft; pi.point.y += offsetTop; }); this.modelConnectorsWithoutEndItemInfo.forEach(pi => { pi.point.x += offsetLeft; pi.point.y += offsetTop; }); } finish(): void { this.draggingConnectors.forEach(c => c.connector.unlockCreateRenderPoints()); } private moveConnector(dc: DraggingConnector, shouldClone: boolean, getMovePoint: (point: Point) => Point) { const startPoints = dc.startPoints; const offset = Vector.fromPoints(startPoints[0].clone(), getMovePoint(startPoints[0]).clone()); if(offset.x || offset.y) this.moveConnectorCore(dc.connector, startPoints, dc.startRenderContext, offset, shouldClone); } private moveConnectorCore(connector: Connector, startPoints: Point[], startRenderContext: ConnectorRenderPointsContext | undefined, offset: Vector, shouldClone: boolean) : void { if(shouldClone || ModelUtils.canMoveConnector(this.selectedItems, connector)) this.offsetConnector(connector, startPoints, startRenderContext, offset); else this.changeConnector(connector); } private moveShape(ds: DraggingShape, getMovePoint: (point: Point) => Point, resetTargetCallback: () => void, updateTargetCallback: (shape: Shape, connectionPointIndex: number) => void) { const shape = ds.shape; const position = getMovePoint(ds.startPosition); ModelUtils.setShapePosition(this.history, this.model, shape, position); ModelUtils.updateMovingShapeConnections(this.history, shape, this.modelConnectorsWithoutBeginItemInfo, this.modelConnectorsWithoutEndItemInfo, resetTargetCallback, updateTargetCallback, (connector) => this.permissionsProvider.addInteractingItem(connector) ); if(!this.draggingConnectors.filter(dc => !!this.selectedItems[dc.connector.key]).length) ModelUtils.updateShapeAttachedConnectors(this.history, this.model, shape); } private offsetConnector(connector: Connector, startPoints: Point[], startRenderContext: ConnectorRenderPointsContext | undefined, offset: Vector) { const newPoints = startPoints.map(p => this.offsetPoint(p, offset)); if(!newPoints[0].equals(connector.points[0])) this.history.addAndRedo( new ChangeConnectorPointsHistoryItem( connector.key, newPoints, this.offsetRenderContext(startRenderContext, offset) ) ); } private offsetRenderContext(context: ConnectorRenderPointsContext | undefined, offset: Vector) : ConnectorRenderPointsContext { if(context === undefined) return undefined; return new ConnectorRenderPointsContext(context.renderPoints.map(p => { const newPoint = this.offsetPoint(p, offset); return new ConnectorRenderPoint(newPoint.x, newPoint.y, p.pointIndex, p.skipped); }), true, context.actualRoutingMode); } private offsetPoint(point: Point, offset: Vector): Point { const pointOffset = Vector.fromPoints(point, this.startPoint); return this.startPoint.clone().offset(offset.x - pointOffset.x, offset.y - pointOffset.y); } private changeConnector(connector: Connector) { ModelUtils.tryRemoveConnectorIntermediatePoints(this.history, connector); ModelUtils.updateConnectorAttachedPoints(this.history, this.model, connector); } private registerConnector(connector: Connector) { this.draggingConnectorsIndexByKey[connector.key] = this.draggingConnectors.push(new DraggingConnector(connector)) - 1; } private createModelConnectorsWithoutBeginItemInfo(): { connector: Connector, point: Point }[] { const connectors = this.model.findConnectorsCore(c => !c.beginItem && !this.containsDraggingConnectorByKey(c.key)); return connectors.map(c => { return { connector: c, point: c.points[0].clone() }; }); } private createModelConnectorsWithoutEndItemInfo(): { connector: Connector, point: Point }[] { const connectors = this.model.findConnectorsCore(c => !c.endItem && !this.containsDraggingConnectorByKey(c.key)); return connectors.map(c => { return { connector: c, point: c.points[c.points.length - 1].clone() }; }); } } export class DraggingConnector { readonly startPoints: Point[]; readonly startRenderContext: ConnectorRenderPointsContext | undefined; constructor(readonly connector: Connector) { this.startPoints = connector.points.map(x => x.clone()); this.startRenderContext = connector.tryCreateRenderPointsContext(); } } class DraggingShape { readonly startPosition: Point; constructor(public readonly shape: Shape) { this.startPosition = shape.position.clone(); } }