blob: 7884d6263f7c79e237575c649b195b517a3a2116 [file] [log] [blame]
/*******************************************************************************
* Copyright (C) 2007 The University of Manchester
*
* Modifications to the initial code base are copyright of their
* respective authors, or their employers as appropriate.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
******************************************************************************/
package net.sf.taverna.t2.workbench.models.graph.svg;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGGraphSettings.SELECTED_COLOUR;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.adjustPathLength;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.animate;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.calculateAngle;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.createAnimationElement;
import static net.sf.taverna.t2.workbench.models.graph.svg.SVGUtil.getHexValue;
import static org.apache.batik.util.CSSConstants.CSS_BLACK_VALUE;
import static org.apache.batik.util.CSSConstants.CSS_DISPLAY_PROPERTY;
import static org.apache.batik.util.CSSConstants.CSS_INLINE_VALUE;
import static org.apache.batik.util.CSSConstants.CSS_NONE_VALUE;
import static org.apache.batik.util.SMILConstants.SMIL_ADDITIVE_ATTRIBUTE;
import static org.apache.batik.util.SMILConstants.SMIL_SUM_VALUE;
import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TAG;
import static org.apache.batik.util.SVGConstants.SVG_ANIMATE_TRANSFORM_TAG;
import static org.apache.batik.util.SVGConstants.SVG_CLICK_EVENT_TYPE;
import static org.apache.batik.util.SVGConstants.SVG_CX_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_CY_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_D_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_FILL_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_MOUSEDOWN_EVENT_TYPE;
import static org.apache.batik.util.SVGConstants.SVG_NONE_VALUE;
import static org.apache.batik.util.SVGConstants.SVG_POINTS_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_RX_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_RY_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_STROKE_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_STROKE_DASHARRAY_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_STROKE_WIDTH_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_TRANSFORM_ATTRIBUTE;
import static org.apache.batik.util.SVGConstants.SVG_ZERO_VALUE;
import static org.apache.batik.util.SVGConstants.TRANSFORM_ROTATE;
import static org.apache.batik.util.SVGConstants.TRANSFORM_TRANSLATE;
import java.awt.Color;
import java.awt.Point;
import java.util.List;
import net.sf.taverna.t2.workbench.models.graph.GraphEdge;
import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseClickEventListener;
import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseDownEventListener;
import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOutEventListener;
import net.sf.taverna.t2.workbench.models.graph.svg.event.SVGMouseOverEventListener;
import org.apache.batik.dom.svg.SVGGraphicsElement;
import org.apache.batik.dom.svg.SVGOMAnimationElement;
import org.apache.batik.dom.svg.SVGOMEllipseElement;
import org.apache.batik.dom.svg.SVGOMGElement;
import org.apache.batik.dom.svg.SVGOMPathElement;
import org.apache.batik.dom.svg.SVGOMPolygonElement;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.svg.SVGElement;
/**
* SVG representation of a graph edge.
*
* @author David Withers
*/
public class SVGGraphEdge extends GraphEdge {
private static final String ARROW_LENGTH = "8.5";
private static final String ARROW_WIDTH = "3";
private static final String ELLIPSE_RADIUS = "3.5";
private SVGGraphController graphController;
private SVGGraphElementDelegate delegate;
private SVGMouseClickEventListener mouseClickAction;
private SVGMouseDownEventListener mouseDownAction;
@SuppressWarnings("unused")
private SVGMouseOverEventListener mouseOverAction;
@SuppressWarnings("unused")
private SVGMouseOutEventListener mouseOutAction;
private SVGOMGElement mainGroup;
private SVGOMPathElement path, deleteButton;
private SVGOMPolygonElement polygon;
private SVGOMEllipseElement ellipse;
private SVGGraphicsElement arrowHead;
private SVGOMAnimationElement animatePath, animatePosition, animateRotation;
public SVGGraphEdge(SVGGraphController graphController) {
super(graphController);
this.graphController = graphController;
mouseClickAction = new SVGMouseClickEventListener(this);
mouseDownAction = new SVGMouseDownEventListener(this);
mouseOverAction = new SVGMouseOverEventListener(this);
mouseOutAction = new SVGMouseOutEventListener(this);
mainGroup = graphController.createGElem();
mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, CSS_BLACK_VALUE);
mainGroup.setAttribute(SVG_STROKE_DASHARRAY_ATTRIBUTE, CSS_NONE_VALUE);
mainGroup.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1");
path = graphController.createPath();
path.setAttribute(SVG_FILL_ATTRIBUTE, SVG_NONE_VALUE);
EventTarget t = (EventTarget) path;
t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false);
// t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false);
// t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false);
mainGroup.appendChild(path);
polygon = graphController.createPolygon();
polygon.setAttribute(SVG_POINTS_ATTRIBUTE, ARROW_LENGTH + ", 0"
+ " 0, -" + ARROW_WIDTH + " 0," + ARROW_WIDTH);
t = (EventTarget) polygon;
t.addEventListener(SVG_CLICK_EVENT_TYPE, mouseClickAction, false);
t.addEventListener(SVG_MOUSEDOWN_EVENT_TYPE, mouseDownAction, false);
// t.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, mouseOverAction, false);
// t.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, mouseOutAction, false);
ellipse = graphController.createEllipse();
ellipse.setAttribute(SVG_CX_ATTRIBUTE, ELLIPSE_RADIUS);
ellipse.setAttribute(SVG_CY_ATTRIBUTE, SVG_ZERO_VALUE);
ellipse.setAttribute(SVG_RX_ATTRIBUTE, ELLIPSE_RADIUS);
ellipse.setAttribute(SVG_RY_ATTRIBUTE, ELLIPSE_RADIUS);
arrowHead = polygon;
mainGroup.appendChild(arrowHead);
deleteButton = graphController.createPath();
deleteButton.setAttribute(SVG_STROKE_ATTRIBUTE, "red");
deleteButton.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2");
deleteButton.setAttribute(SVG_D_ATTRIBUTE,
"M-3.5,-7L3.5,0M-3.5,0L3.5,-7");
deleteButton.setAttribute(CSS_DISPLAY_PROPERTY, CSS_NONE_VALUE);
mainGroup.appendChild(deleteButton);
animatePath = createAnimationElement(graphController, SVG_ANIMATE_TAG,
SVG_D_ATTRIBUTE, null);
animatePosition = createAnimationElement(graphController,
SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
TRANSFORM_TRANSLATE);
animateRotation = createAnimationElement(graphController,
SVG_ANIMATE_TRANSFORM_TAG, SVG_TRANSFORM_ATTRIBUTE,
TRANSFORM_ROTATE);
animateRotation.setAttribute(SMIL_ADDITIVE_ATTRIBUTE, SMIL_SUM_VALUE);
delegate = new SVGGraphElementDelegate(graphController, this, mainGroup);
}
public SVGElement getSVGElement() {
return mainGroup;
}
/**
* Returns the path.
*
* @return the path
*/
public SVGOMPathElement getPathElement() {
return path;
}
@Override
public void setSelected(boolean selected) {
super.setSelected(selected);
final String color = selected ? SELECTED_COLOUR
: getHexValue(getColor());
graphController.updateSVGDocument(new Runnable() {
@Override
public void run() {
mainGroup.setAttribute(SVG_STROKE_ATTRIBUTE, color);
mainGroup.setAttribute(SVG_FILL_ATTRIBUTE, color);
}
});
}
@Override
public void setActive(final boolean active) {
super.setActive(active);
graphController.updateSVGDocument(new Runnable() {
@Override
public void run() {
if (active) {
path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "2");
deleteButton.setAttribute(CSS_DISPLAY_PROPERTY,
CSS_INLINE_VALUE);
} else {
path.setAttribute(SVG_STROKE_WIDTH_ATTRIBUTE, "1");
deleteButton.setAttribute(CSS_DISPLAY_PROPERTY,
CSS_NONE_VALUE);
}
}
});
}
@Override
public void setArrowHeadStyle(final ArrowStyle arrowHeadStyle) {
super.setArrowHeadStyle(arrowHeadStyle);
graphController.updateSVGDocument(new Runnable() {
@Override
public void run() {
if (ArrowStyle.NONE.equals(arrowHeadStyle))
mainGroup.removeChild(arrowHead);
else if (ArrowStyle.NORMAL.equals(arrowHeadStyle)) {
mainGroup.removeChild(arrowHead);
arrowHead = polygon;
mainGroup.appendChild(arrowHead);
} else if (ArrowStyle.DOT.equals(arrowHeadStyle)) {
mainGroup.removeChild(arrowHead);
arrowHead = ellipse;
mainGroup.appendChild(arrowHead);
}
}
});
}
@Override
public void setPath(final List<Point> pointList) {
if (pointList == null)
return;
final List<Point> oldPointList = getPath();
super.setPath(pointList);
graphController.updateSVGDocument(new Runnable() {
@Override
public void run() {
Point lastPoint = pointList.get(pointList.size() - 1);
double angle = calculateAngle(pointList);
if (graphController.isAnimatable() && oldPointList != null) {
adjustPathLength(oldPointList, pointList.size());
Point oldLastPoint = oldPointList.get(oldPointList.size() - 1);
double oldAngle = calculateAngle(oldPointList);
animate(animatePath, path,
graphController.getAnimationSpeed(),
SVGUtil.getPath(oldPointList),
SVGUtil.getPath(pointList));
animate(animatePosition, polygon,
graphController.getAnimationSpeed(), oldLastPoint.x
+ ", " + oldLastPoint.y, lastPoint.x + ", "
+ lastPoint.y);
animate(animateRotation, polygon,
graphController.getAnimationSpeed(), oldAngle
+ " 0 0", angle + " 0 0");
ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+ lastPoint.x + " " + lastPoint.y + ") rotate("
+ angle + " 0 0) ");
deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
"translate(" + lastPoint.x + " " + lastPoint.y
+ ")");
} else {
path.setAttribute(SVG_D_ATTRIBUTE,
SVGUtil.getPath(pointList));
polygon.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+ lastPoint.x + " " + lastPoint.y + ") rotate("
+ angle + " 0 0) ");
ellipse.setAttribute(SVG_TRANSFORM_ATTRIBUTE, "translate("
+ lastPoint.x + " " + lastPoint.y + ") rotate("
+ angle + " 0 0) ");
deleteButton.setAttribute(SVG_TRANSFORM_ATTRIBUTE,
"translate(" + lastPoint.x + " " + lastPoint.y
+ ")");
}
}
});
}
@Override
public void setColor(final Color color) {
delegate.setColor(color);
super.setColor(color);
}
@Override
public void setFillColor(final Color fillColor) {
delegate.setFillColor(fillColor);
super.setFillColor(fillColor);
}
@Override
public void setVisible(final boolean visible) {
delegate.setVisible(visible);
super.setVisible(visible);
}
@Override
public void setFiltered(final boolean filtered) {
delegate.setFiltered(filtered);
super.setFiltered(filtered);
}
@Override
public void setOpacity(final float opacity) {
delegate.setOpacity(opacity);
super.setOpacity(opacity);
}
}