/**
* $Id: mxVmlCanvas.java,v 1.1 2012/11/15 13:26:47 gaudenz Exp $
* Copyright (c) 2007, Gaudenz Alder
*/
package com.mxgraph.canvas;
import com.mxgraph.util.mxConstants;
import com.mxgraph.util.mxPoint;
import com.mxgraph.util.mxRectangle;
import com.mxgraph.util.mxUtils;
import com.mxgraph.view.mxCellState;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.awt.*;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* An implementation of a canvas that uses VML for painting.
*/
public class mxVmlCanvas extends mxBasicCanvas {
/**
* Holds the HTML document that represents the canvas.
*/
protected Document document;
/**
* Constructs a new VML canvas for the specified dimension and scale.
*/
public mxVmlCanvas() {
this(null);
}
/**
* Constructs a new VML canvas for the specified bounds, scale and
* background color.
*/
public mxVmlCanvas(Document document) {
setDocument(document);
}
/**
*
*/
public void setDocument(Document document) {
this.document = document;
}
/**
* Returns a reference to the document that represents the canvas.
*
* @return Returns the document.
*/
public Document getDocument() {
return document;
}
/**
*
*/
public void appendVmlElement(Element node) {
if (document != null) {
Node body = document.getDocumentElement().getFirstChild().getNextSibling();
if (body != null) {
body.appendChild(node);
}
}
}
/* (non-Javadoc)
* @see com.mxgraph.canvas.mxICanvas#drawCell()
*/
public Object drawCell(mxCellState state) {
Map<String, Object> style = state.getStyle();
Element elem = null;
if (state.getAbsolutePointCount() > 1) {
List<mxPoint> pts = state.getAbsolutePoints();
// Transpose all points by cloning into a new array
pts = mxUtils.translatePoints(pts, translate.x, translate.y);
// Draws the line
elem = drawLine(pts, style);
Element strokeNode = document.createElement("v:stroke");
// Draws the markers
String start = mxUtils.getString(style, mxConstants.STYLE_STARTARROW);
String end = mxUtils.getString(style, mxConstants.STYLE_ENDARROW);
if (start != null || end != null) {
if (start != null) {
strokeNode.setAttribute("startarrow", start);
String startWidth = "medium";
String startLength = "medium";
double startSize = mxUtils.getFloat(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE) * scale;
if (startSize < 6) {
startWidth = "narrow";
startLength = "short";
}
else if (startSize > 10) {
startWidth = "wide";
startLength = "long";
}
strokeNode.setAttribute("startarrowwidth", startWidth);
strokeNode.setAttribute("startarrowlength", startLength);
}
if (end != null) {
strokeNode.setAttribute("endarrow", end);
String endWidth = "medium";
String endLength = "medium";
double endSize = mxUtils.getFloat(style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE) * scale;
if (endSize < 6) {
endWidth = "narrow";
endLength = "short";
}
else if (endSize > 10) {
endWidth = "wide";
endLength = "long";
}
strokeNode.setAttribute("endarrowwidth", endWidth);
strokeNode.setAttribute("endarrowlength", endLength);
}
}
if (mxUtils.isTrue(style, mxConstants.STYLE_DASHED)) {
strokeNode.setAttribute("dashstyle", "2 2");
}
elem.appendChild(strokeNode);
}
else {
int x = (int)state.getX() + translate.x;
int y = (int)state.getY() + translate.y;
int w = (int)state.getWidth();
int h = (int)state.getHeight();
if (!mxUtils.getString(style, mxConstants.STYLE_SHAPE, "").equals(mxConstants.SHAPE_SWIMLANE)) {
elem = drawShape(x, y, w, h, style);
if (mxUtils.isTrue(style, mxConstants.STYLE_DASHED)) {
Element strokeNode = document.createElement("v:stroke");
strokeNode.setAttribute("dashstyle", "2 2");
elem.appendChild(strokeNode);
}
}
else {
int start = (int)Math.round(mxUtils.getInt(style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE) * scale);
// Removes some styles to draw the content area
Map<String, Object> cloned = new Hashtable<String, Object>(style);
cloned.remove(mxConstants.STYLE_FILLCOLOR);
cloned.remove(mxConstants.STYLE_ROUNDED);
if (mxUtils.isTrue(style, mxConstants.STYLE_HORIZONTAL, true)) {
elem = drawShape(x, y, w, start, style);
drawShape(x, y + start, w, h - start, cloned);
}
else {
elem = drawShape(x, y, start, h, style);
drawShape(x + start, y, w - start, h, cloned);
}
}
}
return elem;
}
/*
* (non-Javadoc)
* @see com.mxgraph.canvas.mxICanvas#drawLabel()
*/
public Object drawLabel(String label, mxCellState state, boolean html) {
mxRectangle bounds = state.getLabelBounds();
if (drawLabels && bounds != null) {
int x = (int)bounds.getX() + translate.x;
int y = (int)bounds.getY() + translate.y;
int w = (int)bounds.getWidth();
int h = (int)bounds.getHeight();
Map<String, Object> style = state.getStyle();
return drawText(label, x, y, w, h, style);
}
return null;
}
/**
* Draws the shape specified with the STYLE_SHAPE key in the given style.
*
* @param x X-coordinate of the shape.
* @param y Y-coordinate of the shape.
* @param w Width of the shape.
* @param h Height of the shape.
* @param style Style of the the shape.
*/
public Element drawShape(int x, int y, int w, int h, Map<String, Object> style) {
String fillColor = mxUtils.getString(style, mxConstants.STYLE_FILLCOLOR);
String strokeColor = mxUtils.getString(style, mxConstants.STYLE_STROKECOLOR);
float strokeWidth = (float)(mxUtils.getFloat(style, mxConstants.STYLE_STROKEWIDTH, 1) * scale);
// Draws the shape
String shape = mxUtils.getString(style, mxConstants.STYLE_SHAPE);
Element elem = null;
if (shape.equals(mxConstants.SHAPE_IMAGE)) {
String img = getImageForStyle(style);
if (img != null) {
elem = document.createElement("v:img");
elem.setAttribute("src", img);
}
}
else if (shape.equals(mxConstants.SHAPE_LINE)) {
String direction = mxUtils.getString(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST);
String points = null;
if (direction.equals(mxConstants.DIRECTION_EAST) || direction.equals(mxConstants.DIRECTION_WEST)) {
int mid = Math.round(h / 2);
points = "m 0 " + mid + " l " + w + " " + mid;
}
else {
int mid = Math.round(w / 2);
points = "m " + mid + " 0 L " + mid + " " + h;
}
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_ELLIPSE)) {
elem = document.createElement("v:oval");
}
else if (shape.equals(mxConstants.SHAPE_DOUBLE_ELLIPSE)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
int inset = (int)((3 + strokeWidth) * scale);
String points = "ar 0 0 " +
w +
" " +
h +
" 0 " +
(h / 2) +
" " +
(w / 2) +
" " +
(h / 2) +
" e ar " +
inset +
" " +
inset +
" " +
(w - inset) +
" " +
(h - inset) +
" 0 " +
(h / 2) +
" " +
(w / 2) +
" " +
(h / 2);
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_RHOMBUS)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
String points = "m " + (w / 2) + " 0 l " + w + " " + (h / 2) + " l " + (w / 2) + " " + h + " l 0 " + (h / 2);
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_TRIANGLE)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
String direction = mxUtils.getString(style, mxConstants.STYLE_DIRECTION, "");
String points = null;
if (direction.equals(mxConstants.DIRECTION_NORTH)) {
points = "m 0 " + h + " l " + (w / 2) + " 0 " + " l " + w + " " + h;
}
else if (direction.equals(mxConstants.DIRECTION_SOUTH)) {
points = "m 0 0 l " + (w / 2) + " " + h + " l " + w + " 0";
}
else if (direction.equals(mxConstants.DIRECTION_WEST)) {
points = "m " + w + " 0 l " + w + " " + (h / 2) + " l " + w + " " + h;
}
else
// east
{
points = "m 0 0 l " + w + " " + (h / 2) + " l 0 " + h;
}
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_HEXAGON)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
String direction = mxUtils.getString(style, mxConstants.STYLE_DIRECTION, "");
String points = null;
if (direction.equals(mxConstants.DIRECTION_NORTH) || direction.equals(mxConstants.DIRECTION_SOUTH)) {
points = "m " +
(int)(0.5 * w) +
" 0 l " +
w +
" " +
(int)(0.25 * h) +
" l " +
w +
" " +
(int)(0.75 * h) +
" l " +
(int)(0.5 * w) +
" " +
h +
" l 0 " +
(int)(0.75 * h) +
" l 0 " +
(int)(0.25 * h);
}
else {
points = "m " +
(int)(0.25 * w) +
" 0 l " +
(int)(0.75 * w) +
" 0 l " +
w +
" " +
(int)(0.5 * h) +
" l " +
(int)(0.75 * w) +
" " +
h +
" l " +
(int)(0.25 * w) +
" " +
h +
" l 0 " +
(int)(0.5 * h);
}
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_CLOUD)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
String points = "m " +
(int)(0.25 * w) +
" " +
(int)(0.25 * h) +
" c " +
(int)(0.05 * w) +
" " +
(int)(0.25 * h) +
" 0 " +
(int)(0.5 * h) +
" " +
(int)(0.16 * w) +
" " +
(int)(0.55 * h) +
" c 0 " +
(int)(0.66 * h) +
" " +
(int)(0.18 * w) +
" " +
(int)(0.9 * h) +
" " +
(int)(0.31 * w) +
" " +
(int)(0.8 * h) +
" c " +
(int)(0.4 * w) +
" " +
(h) +
" " +
(int)(0.7 * w) +
" " +
(h) +
" " +
(int)(0.8 * w) +
" " +
(int)(0.8 * h) +
" c " +
(w) +
" " +
(int)(0.8 * h) +
" " +
(w) +
" " +
(int)(0.6 * h) +
" " +
(int)(0.875 * w) +
" " +
(int)(0.5 * h) +
" c " +
(w) +
" " +
(int)(0.3 * h) +
" " +
(int)(0.8 * w) +
" " +
(int)(0.1 * h) +
" " +
(int)(0.625 * w) +
" " +
(int)(0.2 * h) +
" c " +
(int)(0.5 * w) +
" " +
(int)(0.05 * h) +
" " +
(int)(0.3 * w) +
" " +
(int)(0.05 * h) +
" " +
(int)(0.25 * w) +
" " +
(int)(0.25 * h);
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_ACTOR)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
double width3 = w / 3;
String points = "m 0 " +
(h) +
" C 0 " +
(3 * h / 5) +
" 0 " +
(2 * h / 5) +
" " +
(w / 2) +
" " +
(2 * h / 5) +
" c " +
(int)(w / 2 - width3) +
" " +
(2 * h / 5) +
" " +
(int)(w / 2 - width3) +
" 0 " +
(w / 2) +
" 0 c " +
(int)(w / 2 + width3) +
" 0 " +
(int)(w / 2 + width3) +
" " +
(2 * h / 5) +
" " +
(w / 2) +
" " +
(2 * h / 5) +
" c " +
(w) +
" " +
(2 * h / 5) +
" " +
(w) +
" " +
(3 * h / 5) +
" " +
(w) +
" " +
(h);
elem.setAttribute("path", points + " x e");
}
else if (shape.equals(mxConstants.SHAPE_CYLINDER)) {
elem = document.createElement("v:shape");
elem.setAttribute("coordsize", w + " " + h);
double dy = Math.min(40, Math.floor(h / 5));
String points = "m 0 " +
(int)(dy) +
" C 0 " +
(int)(dy / 3) +
" " +
(w) +
" " +
(int)(dy / 3) +
" " +
(w) +
" " +
(int)(dy) +
" L " +
(w) +
" " +
(int)(h - dy) +
" C " +
(w) +
" " +
(int)(h + dy / 3) +
" 0 " +
(int)(h + dy / 3) +
" 0 " +
(int)(h - dy) +
" x e" +
" m 0 " +
(int)(dy) +
" C 0 " +
(int)(2 * dy) +
" " +
(w) +
" " +
(int)(2 * dy) +
" " +
(w) +
" " +
(int)(dy);
elem.setAttribute("path", points + " e");
}
else {
if (mxUtils.isTrue(style, mxConstants.STYLE_ROUNDED, false)) {
elem = document.createElement("v:roundrect");
elem.setAttribute("arcsize", (mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) + "%");
}
else {
elem = document.createElement("v:rect");
}
}
String s = "position:absolute;left:" +
String.valueOf(x) +
"px;top:" +
String.valueOf(y) +
"px;width:" +
String.valueOf(w) +
"px;height:" +
String.valueOf(h) +
"px;";
// Applies rotation
double rotation = mxUtils.getDouble(style, mxConstants.STYLE_ROTATION);
if (rotation != 0) {
s += "rotation:" + rotation + ";";
}
elem.setAttribute("style", s);
// Adds the shadow element
if (mxUtils.isTrue(style, mxConstants.STYLE_SHADOW, false) && fillColor != null) {
Element shadow = document.createElement("v:shadow");
shadow.setAttribute("on", "true");
shadow.setAttribute("color", mxConstants.W3C_SHADOWCOLOR);
elem.appendChild(shadow);
}
float opacity = mxUtils.getFloat(style, mxConstants.STYLE_OPACITY, 100);
// Applies opacity to fill
if (fillColor != null) {
Element fill = document.createElement("v:fill");
fill.setAttribute("color", fillColor);
if (opacity != 100) {
fill.setAttribute("opacity", String.valueOf(opacity / 100));
}
elem.appendChild(fill);
}
else {
elem.setAttribute("filled", "false");
}
// Applies opacity to stroke
if (strokeColor != null) {
elem.setAttribute("strokecolor", strokeColor);
Element stroke = document.createElement("v:stroke");
if (opacity != 100) {
stroke.setAttribute("opacity", String.valueOf(opacity / 100));
}
elem.appendChild(stroke);
}
else {
elem.setAttribute("stroked", "false");
}
elem.setAttribute("strokeweight", String.valueOf(strokeWidth) + "px");
appendVmlElement(elem);
return elem;
}
/**
* Draws the given lines as segments between all points of the given list
* of mxPoints.
*
* @param pts List of points that define the line.
* @param style Style to be used for painting the line.
*/
public Element drawLine(List<mxPoint> pts, Map<String, Object> style) {
String strokeColor = mxUtils.getString(style, mxConstants.STYLE_STROKECOLOR);
float strokeWidth = (float)(mxUtils.getFloat(style, mxConstants.STYLE_STROKEWIDTH, 1) * scale);
Element elem = document.createElement("v:shape");
if (strokeColor != null && strokeWidth > 0) {
mxPoint pt = pts.get(0);
Rectangle r = new Rectangle(pt.getPoint());
StringBuilder buf = new StringBuilder("m " + Math.round(pt.getX()) + " " + Math.round(pt.getY()));
for (int i = 1; i < pts.size(); i++) {
pt = pts.get(i);
buf.append(" l " + Math.round(pt.getX()) + " " + Math.round(pt.getY()));
r = r.union(new Rectangle(pt.getPoint()));
}
String d = buf.toString();
elem.setAttribute("path", d);
elem.setAttribute("filled", "false");
elem.setAttribute("strokecolor", strokeColor);
elem.setAttribute("strokeweight", String.valueOf(strokeWidth) + "px");
String s = "position:absolute;" +
"left:" +
String.valueOf(r.x) +
"px;" +
"top:" +
String.valueOf(r.y) +
"px;" +
"width:" +
String.valueOf(r.width) +
"px;" +
"height:" +
String.valueOf(r.height) +
"px;";
elem.setAttribute("style", s);
elem.setAttribute("coordorigin", String.valueOf(r.x) + " " + String.valueOf(r.y));
elem.setAttribute("coordsize", String.valueOf(r.width) + " " + String.valueOf(r.height));
}
appendVmlElement(elem);
return elem;
}
/**
* Draws the specified text either using drawHtmlString or using drawString.
*
* @param text Text to be painted.
* @param x X-coordinate of the text.
* @param y Y-coordinate of the text.
* @param w Width of the text.
* @param h Height of the text.
* @param style Style to be used for painting the text.
*/
public Element drawText(String text, int x, int y, int w, int h, Map<String, Object> style) {
Element table = mxUtils.createTable(document, text, x, y, w, h, scale, style);
appendVmlElement(table);
return table;
}
}