devexpress-diagram
Version:
DevExpress Diagram Control
267 lines (257 loc) • 14.2 kB
text/typescript
import { SvgPrimitive } from "../../../Render/Primitives/Primitive";
import { Shape } from "../Shape";
import { ImagePrimitive } from "../../../Render/Primitives/ImagePrimitive";
import { Rectangle } from "@devexpress/utils/lib/geometry/rectangle";
import { Size } from "@devexpress/utils/lib/geometry/size";
import { Point } from "@devexpress/utils/lib/geometry/point";
import { ConnectionPoint } from "../../ConnectionPoint";
import { ConnectionPointSide } from "../../DiagramItem";
import { ShapeWithImageDescription } from "./ShapeWithImageDescription";
import { ICustomShape } from "../../../Interfaces";
import { ShapeDescription, ShapeDefaultDimension } from "./ShapeDescription";
import { ImageInfo } from "../../../Images/ImageInfo";
import { ImageCache, CacheImageInfo } from "../../../Images/ImageCache";
import { ImageLoader } from "../../../Images/ImageLoader";
import { ShapeParameters } from "../ShapeParameters";
import { ShapeParameterPoint } from "../ShapeParameterPoint";
import { SvgElementPrimitive } from "../../../Render/Primitives/SvgElementPrimitive";
import { TextAngle } from "../../../Render/Primitives/TextPrimitive";
export class CustomShapeDescription extends ShapeWithImageDescription {
public title: string;
public defaultText: string
public defaultImageUrl: string
private svgImage: ImageInfo;
private svgToolboxImage: ImageInfo;
private imageLoader = new ImageLoader(this.updateSvgImage.bind(this));
constructor(public properties: ICustomShape, public baseDescription?: ShapeDescription) {
super(
new Size(
properties.defaultWidth || baseDescription && baseDescription.defaultSize.width || ShapeDefaultDimension,
properties.defaultHeight || baseDescription && baseDescription.defaultSize.height || ShapeDefaultDimension
)
);
this.defaultText = properties.defaultText !== undefined ? properties.defaultText : baseDescription && baseDescription.getDefaultText();
this.defaultImageUrl = properties.defaultImageUrl || baseDescription && baseDescription.getDefaultImageUrl();
this.title = properties.title || baseDescription && baseDescription.getTitle() || this.defaultText || "";
this.connectionPoints = this.createConnectionPoints();
if(properties.svgUrl) {
this.svgImage = new ImageInfo(properties.svgUrl);
const cachedImage = ImageCache.instance.createUnloadedInfoByShapeImageInfo(this.svgImage);
this.imageLoader.load(cachedImage);
}
if(properties.svgToolboxUrl) {
this.svgToolboxImage = new ImageInfo(properties.svgToolboxUrl);
const cachedImage = ImageCache.instance.createUnloadedInfoByShapeImageInfo(this.svgToolboxImage);
this.imageLoader.load(cachedImage);
}
}
get key() { return this.properties.type; }
get allowEditText() { return this.properties.allowEditText !== false; }
get allowEditImage() { return this.baseDescription ? this.baseDescription.allowEditImage : this.properties.allowEditImage === true; }
get enableChildren() { return this.baseDescription && this.baseDescription.enableChildren; }
get hasTemplate() { return !!this.properties.createTemplate; }
get minWidth() { return this.properties.minWidth || this.baseDescription && this.baseDescription.minWidth; }
get minHeight() { return this.properties.minHeight || this.baseDescription && this.baseDescription.minHeight; }
get maxWidth() { return this.properties.maxWidth || this.baseDescription && this.baseDescription.maxWidth; }
get maxHeight() { return this.properties.maxHeight || this.baseDescription && this.baseDescription.maxHeight; }
get keepRatioOnAutoSize(): boolean { return this.properties.keepRatioOnAutoSize; }
get toolboxSize() {
if(this.properties.toolboxWidthToHeightRatio)
return new Size(this.defaultSize.width, this.defaultSize.width / this.properties.toolboxWidthToHeightRatio);
return this.defaultSize;
}
getTextAngle(): TextAngle {
return (this.baseDescription && this.baseDescription.getTextAngle()) || super.getTextAngle();
}
getTitle() {
return this.title !== undefined ? this.title : super.getTitle();
}
getDefaultText() {
return this.defaultText !== undefined ? this.defaultText : super.getDefaultText();
}
getDefaultImageUrl() {
return this.defaultImageUrl !== undefined ? this.defaultImageUrl : super.getDefaultImageUrl();
}
allowResizeHorizontally(shape: Shape) {
if(this.properties.allowResize === false)
return false;
if(this.baseDescription)
return this.baseDescription.allowResizeHorizontally(shape);
return super.allowResizeHorizontally(shape);
}
allowResizeVertically(shape: Shape) {
if(this.properties.allowResize === false)
return false;
if(this.baseDescription)
return this.baseDescription.allowResizeVertically(shape);
return super.allowResizeVertically(shape);
}
protected createConnectionPoints(): ConnectionPoint[] {
if(this.properties && this.properties.connectionPoints && this.properties.connectionPoints.length)
return this.properties.connectionPoints.map(ptObj => {
if(ptObj && typeof ptObj["x"] === "number" && typeof ptObj["y"] === "number") {
const side = typeof ptObj["side"] === "number" ? ptObj["side"] : ConnectionPointSide.Undefined;
return new ConnectionPoint(ptObj["x"], ptObj["y"], side);
}
}).filter(pt => pt);
return super.createConnectionPoints();
}
private getConnectionPointSides(): {[key: number]: number[]} {
const result: {[key: number]: number[]} = {};
for(let i = 0; i < this.connectionPoints.length; i++) {
const pointSide = ShapeDescription.getConnectionPointSideByGeometry(this.connectionPoints[i]);
if(!result[pointSide])
result[pointSide] = [];
result[pointSide].push(i);
}
return result;
}
getConnectionPointIndexForSide(side: ConnectionPointSide): number {
const connectionPointSides = this.getConnectionPointSides();
let pointIndexes = connectionPointSides[side];
if(pointIndexes) {
let index = Math.floor(pointIndexes.length / 2);
if(pointIndexes.length % 2 === 0)
index--;
return pointIndexes[index];
}
else {
pointIndexes = connectionPointSides[(side + 1) % 4];
if(pointIndexes)
return pointIndexes[0];
else {
pointIndexes = connectionPointSides[(side + 3) % 4];
if(pointIndexes)
return pointIndexes[pointIndexes.length - 1];
else {
pointIndexes = connectionPointSides[(side + 2) % 4];
if(pointIndexes)
return pointIndexes[0];
}
}
}
return side;
}
createImagePrimitives(shape: Shape, forToolbox: boolean): SvgPrimitive<SVGGraphicsElement>[] {
if(this.baseDescription)
return this.baseDescription.createImagePrimitives(shape, forToolbox);
return super.createImagePrimitives(shape, forToolbox);
}
createShapePrimitives(shape: Shape, forToolbox?: boolean): SvgPrimitive<SVGGraphicsElement>[] {
let primitives: SvgPrimitive<SVGGraphicsElement>[] = [];
const { x: left, y: top, width, height } = shape.rectangle;
if(this.baseDescription)
primitives = this.baseDescription.createShapePrimitives(shape, forToolbox);
else {
const svgImage = forToolbox && this.svgToolboxImage ? this.svgToolboxImage : this.svgImage;
if(svgImage) {
const svgLeft = left + (this.properties.svgLeft && !forToolbox ? this.properties.svgLeft * width : 0);
const svgTop = top + (this.properties.svgTop && !forToolbox ? this.properties.svgTop * height : 0);
const svgWidth = this.properties.svgWidth && !forToolbox ? this.properties.svgWidth * width : width;
const svgHeight = this.properties.svgHeight && !forToolbox ? this.properties.svgHeight * height : height;
primitives = primitives.concat([
new ImagePrimitive(svgLeft, svgTop, svgWidth, svgHeight, svgImage.exportUrl)
]);
}
}
const createTemplate = forToolbox && this.properties.createToolboxTemplate || this.properties.createTemplate;
if(createTemplate) {
const templateLeft = left + (this.properties.templateLeft && !forToolbox ? this.properties.templateLeft * width : 0);
const templateTop = top + (this.properties.templateTop && !forToolbox ? this.properties.templateTop * height : 0);
const templateWidth = this.properties.templateWidth && !forToolbox ? this.properties.templateWidth * width : width;
const templateHeight = this.properties.templateHeight && !forToolbox ? this.properties.templateHeight * height : height;
const nativeShape = this.properties.apiController ? this.properties.apiController.createNativeShape(shape) : shape.toNative();
primitives = primitives.concat([
new SvgElementPrimitive(
templateLeft, templateTop, templateWidth, templateHeight,
createTemplate, this.properties.destroyTemplate, nativeShape
)
]);
}
return primitives;
}
createParameters(parameters: ShapeParameters) {
if(this.baseDescription)
return this.baseDescription.createParameters(parameters);
else
return super.createParameters(parameters);
}
normalizeParameters(shape: Shape, parameters: ShapeParameters) {
if(this.baseDescription)
this.baseDescription.normalizeParameters(shape, parameters);
else
super.normalizeParameters(shape, parameters);
}
modifyParameters(shape: Shape, parameters: ShapeParameters, deltaX: number, deltaY: number) {
if(this.baseDescription)
this.baseDescription.modifyParameters(shape, parameters, deltaX, deltaY);
else
super.modifyParameters(shape, parameters, deltaX, deltaY);
}
getParameterPoints(shape: Shape): ShapeParameterPoint[] {
if(this.baseDescription)
return this.baseDescription.getParameterPoints(shape);
else
return super.getParameterPoints(shape);
}
getTextRectangle(shape: Shape): Rectangle {
if(this.baseDescription)
return this.baseDescription.getTextRectangle(shape);
else {
const { x: left, y: top, width, height } = shape.rectangle;
return new Rectangle(
left + (this.properties.textLeft ? this.properties.textLeft * width : 0),
top + (this.properties.textTop ? this.properties.textTop * height : 0),
this.properties.textWidth ? this.properties.textWidth * width : width,
this.properties.textHeight ? this.properties.textHeight * height : height,
);
}
}
getSizeByText(textSize: Size, shape: Shape): Size {
if(this.baseDescription)
return this.baseDescription.getSizeByText(textSize, shape);
else {
let textWidth = this.properties.textWidth;
if(!textWidth)
textWidth = 1;
let textHeight = this.properties.textHeight;
if(!textHeight)
textHeight = 1;
return new Size(textSize.width / textWidth, textSize.height / textHeight);
}
}
getImageSize(shapeSize: Size, includeMargins: boolean, forToolbox?: boolean): Size {
if(this.baseDescription) {
if(this.baseDescription instanceof ShapeWithImageDescription)
return this.baseDescription.getImageSize(shapeSize, includeMargins, forToolbox);
return Size.empty();
}
return new Size(
this.properties.imageWidth ? this.properties.imageWidth * shapeSize.width : shapeSize.width,
this.properties.imageHeight ? this.properties.imageHeight * shapeSize.height : shapeSize.height)
.nonNegativeSize();
}
getImagePlacementRectangle(rect: Rectangle, forToolbox?: boolean): Rectangle {
if(this.baseDescription) {
if(this.baseDescription instanceof ShapeWithImageDescription)
return this.baseDescription.getImagePlacementRectangle(rect, forToolbox);
return Rectangle.fromGeometry(Point.zero(), Size.empty());
}
const { x: left, y: top, width, height } = rect;
return new Rectangle(
left + (this.properties.imageLeft && !forToolbox ? this.properties.imageLeft * width : 0),
top + (this.properties.imageTop && !forToolbox ? this.properties.imageTop * height : 0),
this.properties.imageWidth && !forToolbox ? this.properties.imageWidth * width : width,
this.properties.imageHeight && !forToolbox ? this.properties.imageHeight * height : height);
}
updateSvgImage(cacheImageInfo: CacheImageInfo): void {
const isToolboxImage = cacheImageInfo.imageUrl && cacheImageInfo.imageUrl === this.properties.svgToolboxUrl;
const svgImage = isToolboxImage ? this.svgToolboxImage : this.svgImage;
if(cacheImageInfo.base64)
svgImage.loadBase64Content(cacheImageInfo.base64);
else
svgImage.setUnableToLoadFlag();
if(!isToolboxImage)
this.raiseShapeDescriptionChanged(this);
}
}