"use strict"; /* * Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved. */ import * as go from "../release/go"; /** * @constructor * @extends CommandHandler * @class * This CommandHandler class allows the user to position selected Parts in a diagram * relative to the first part selected, in addition to overriding the doKeyDown method * of the CommandHandler for handling the arrow keys in additional manners. *
* Typical usage: *
* $(go.Diagram, "myDiagramDiv",
* {
* commandHandler: $(DrawCommandHandler),
* . . .
* }
* )
*
* or:
* * myDiagram.commandHandler = new DrawCommandHandler(); **/ export class DrawCommandHandler extends go.CommandHandler { private _arrowKeyBehavior: string = "move"; private _pasteOffset: go.Point = new go.Point(10, 10); private _lastPasteOffset: go.Point = new go.Point(0, 0); /** * This controls whether or not the user can invoke the {@link #alignLeft}, {@link #alignRight}, * {@link #alignTop}, {@link #alignBottom}, {@link #alignCenterX}, {@link #alignCenterY} commands. * @this {DrawCommandHandler} * @return {boolean} * This returns true: * if the diagram is not {@link Diagram#isReadOnly}, * if the model is not {@link Model#isReadOnly}, and * if there are at least two selected {@link Part}s. */ public canAlignSelection(): boolean { var diagram = this.diagram; if (diagram === null || diagram.isReadOnly || diagram.isModelReadOnly) return false; if (diagram.selection.count < 2) return false; return true; }; /** * Aligns selected parts along the left-most edge of the left-most part. * @this {DrawCommandHandler} */ public alignLeft() { var diagram = this.diagram; diagram.startTransaction("aligning left"); var minPosition = Infinity; diagram.selection.each( (current) => { if (current instanceof go.Link) return; // skips over go.Link minPosition = Math.min(current.position.x, minPosition); }); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(minPosition, current.position.y)); }); diagram.commitTransaction("aligning left"); }; /** * Aligns selected parts at the right-most edge of the right-most part. * @this {DrawCommandHandler} */ public alignRight() { var diagram = this.diagram; diagram.startTransaction("aligning right"); var maxPosition = -Infinity; diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link var rightSideLoc = current.actualBounds.x + current.actualBounds.width; maxPosition = Math.max(rightSideLoc, maxPosition); }); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(maxPosition - current.actualBounds.width, current.position.y)); }); diagram.commitTransaction("aligning right"); }; /** * Aligns selected parts at the top-most edge of the top-most part. * @this {DrawCommandHandler} */ public alignTop() { var diagram = this.diagram; diagram.startTransaction("alignTop"); var minPosition = Infinity; diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link minPosition = Math.min(current.position.y, minPosition); }); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(current.position.x, minPosition)); }); diagram.commitTransaction("alignTop"); }; /** * Aligns selected parts at the bottom-most edge of the bottom-most part. * @this {DrawCommandHandler} */ public alignBottom() { var diagram = this.diagram; diagram.startTransaction("aligning bottom"); var maxPosition = -Infinity; diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link var bottomSideLoc = current.actualBounds.y + current.actualBounds.height; maxPosition = Math.max(bottomSideLoc, maxPosition); }); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(current.actualBounds.x, maxPosition - current.actualBounds.height)); }); diagram.commitTransaction("aligning bottom"); }; /** * Aligns selected parts at the x-value of the center point of the first selected part. * @this {DrawCommandHandler} */ public alignCenterX() { var diagram = this.diagram; var firstSelection = diagram.selection.first(); if (!firstSelection) return; diagram.startTransaction("aligning Center X"); var centerX = firstSelection.actualBounds.x + firstSelection.actualBounds.width / 2; diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(centerX - current.actualBounds.width / 2, current.actualBounds.y)); }); diagram.commitTransaction("aligning Center X"); }; /** * Aligns selected parts at the y-value of the center point of the first selected part. * @this {DrawCommandHandler} */ public alignCenterY() { var diagram = this.diagram; var firstSelection = diagram.selection.first(); if (!firstSelection) return; diagram.startTransaction("aligning Center Y"); var centerY = firstSelection.actualBounds.y + firstSelection.actualBounds.height / 2; diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link current.move(new go.Point(current.actualBounds.x, centerY - current.actualBounds.height / 2)); }); diagram.commitTransaction("aligning Center Y"); }; /** * Aligns selected parts top-to-bottom in order of the order selected. * Distance between parts can be specified. Default distance is 0. * @this {DrawCommandHandler} * @param {number} distance */ public alignColumn(distance: number) { var diagram = this.diagram; diagram.startTransaction("align Column"); if (distance === undefined) distance = 0; // for aligning edge to edge distance = parseFloat(distance.toString()); var selectedParts = new Array(); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link selectedParts.push(current); }); for (var i = 0; i < selectedParts.length - 1; i++) { var current = selectedParts[i]; // adds distance specified between parts var curBottomSideLoc = current.actualBounds.y + current.actualBounds.height + distance; var next = selectedParts[i + 1]; next.move(new go.Point(current.actualBounds.x, curBottomSideLoc)); } diagram.commitTransaction("align Column"); }; /** * Aligns selected parts left-to-right in order of the order selected. * Distance between parts can be specified. Default distance is 0. * @this {DrawCommandHandler} * @param {number} distance */ public alignRow(distance: number) { if (distance === undefined) distance = 0; // for aligning edge to edge distance = parseFloat(distance.toString()); var diagram = this.diagram; diagram.startTransaction("align Row"); var selectedParts = new Array(); diagram.selection.each((current) => { if (current instanceof go.Link) return; // skips over go.Link selectedParts.push(current); }); for (var i = 0; i < selectedParts.length - 1; i++) { var current = selectedParts[i]; // adds distance specified between parts var curRightSideLoc = current.actualBounds.x + current.actualBounds.width + distance; var next = selectedParts[i + 1]; next.move(new go.Point(curRightSideLoc, current.actualBounds.y)); } diagram.commitTransaction("align Row"); }; /** * This controls whether or not the user can invoke the {@link #rotate} command. * @this {DrawCommandHandler} * @param {number=} angle the positive (clockwise) or negative (counter-clockwise) change in the rotation angle of each Part, in degrees. * @return {boolean} * This returns true: * if the diagram is not {@link Diagram#isReadOnly}, * if the model is not {@link Model#isReadOnly}, and * if there is at least one selected {@link Part}. */ public canRotate(number: number): boolean { var diagram = this.diagram; if (diagram === null || diagram.isReadOnly || diagram.isModelReadOnly) return false; if (diagram.selection.count < 1) return false; return true; }; /** * Change the angle of the parts connected with the given part. This is in the command handler * so it can be easily accessed for the purpose of creating commands that change the rotation of a part. * @this {DrawCommandHandler} * @param {number=} angle the positive (clockwise) or negative (counter-clockwise) change in the rotation angle of each Part, in degrees. */ public rotate(angle: number) { if (angle === undefined) angle = 90; var diagram = this.diagram; diagram.startTransaction("rotate " + angle.toString()); var diagram = this.diagram; diagram.selection.each((current) => { if (current instanceof go.Link || current instanceof go.Group) return; // skips over Links and Groups current.angle += angle; }); diagram.commitTransaction("rotate " + angle.toString()); }; /** * This implements custom behaviors for arrow key keyboard events. * Set {@link #arrowKeyBehavior} to "select", "move" (the default), "scroll" (the standard behavior), or "none" * to affect the behavior when the user types an arrow key. * @this {DrawCommandHandler}*/ public doKeyDown() { var diagram = this.diagram; if (diagram === null) return; var e = diagram.lastInput; // determines the function of the arrow keys if (e.key === "Up" || e.key === "Down" || e.key === "Left" || e.key === "Right") { var behavior = this.arrowKeyBehavior; if (behavior === "none") { // no-op return; } else if (behavior === "select") { this._arrowKeySelect(); return; } else if (behavior === "move") { this._arrowKeyMove(); return; } // otherwise drop through to get the default scrolling behavior } // otherwise still does all standard commands super.doKeyDown.call(this); }; /** * Collects in an Array all of the non-Link Parts currently in the Diagram. * @this {DrawCommandHandler} * @return {Array} */ public _getAllParts(): Array