fiore@0: /*
fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
fiore@0:
fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
fiore@0:
fiore@0: This program is free software: you can redistribute it and/or modify
fiore@0: it under the terms of the GNU General Public License as published by
fiore@0: the Free Software Foundation, either version 3 of the License, or
fiore@0: (at your option) any later version.
fiore@0:
fiore@0: This program is distributed in the hope that it will be useful,
fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of
fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
fiore@0: GNU General Public License for more details.
fiore@0:
fiore@0: You should have received a copy of the GNU General Public License
fiore@0: along with this program. If not, see .
fiore@0: */
fiore@0:
fiore@0: package uk.ac.qmul.eecs.ccmi.simpletemplate;
fiore@0:
fiore@0: import java.awt.Graphics2D;
fiore@0: import java.awt.Stroke;
fiore@0: import java.awt.geom.Line2D;
fiore@0: import java.awt.geom.Point2D;
fiore@0: import java.awt.geom.Rectangle2D;
fiore@0: import java.io.IOException;
fiore@0: import java.io.InputStream;
fiore@0: import java.util.HashMap;
fiore@0: import java.util.List;
fiore@0: import java.util.Map;
fiore@0:
fiore@0: import org.w3c.dom.Document;
fiore@0: import org.w3c.dom.Element;
fiore@0: import org.w3c.dom.NodeList;
fiore@0:
fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode;
fiore@3: import uk.ac.qmul.eecs.ccmi.gui.DiagramEventSource;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Edge;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.GraphElement;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.LineStyle;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Node;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;
fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
fiore@0:
fiore@5: /**
fiore@5: * An edge rendered as a straight, dotted or dashed line. The edge can have an arrow head
fiore@5: * at each end. Possible arrow heads are :
fiore@5: *
fiore@5: * - Triangle
fiore@5: *
- Black Triangle
fiore@5: *
- V
fiore@5: *
- Half V
fiore@5: *
- Diamond
fiore@5: *
- Black Diamond
fiore@5: *
- Tail
fiore@5: *
fiore@5: *
fiore@5: */
fiore@0: @SuppressWarnings("serial")
fiore@0: public class SimpleShapeEdge extends Edge {
fiore@0:
fiore@0: public SimpleShapeEdge(String type, LineStyle style, ArrowHead[] heads, String[] availableEndDescriptions, int minAttachedNodes, int maxAttachedNodes) {
fiore@0: super(type,availableEndDescriptions,minAttachedNodes,maxAttachedNodes,style);
fiore@0: this.heads = heads;
fiore@0: currentHeads = new HashMap();
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@5: public boolean removeNode(DiagramNode n, Object source){
fiore@0: currentHeads.remove(n);
fiore@4: return super.removeNode(n,source);
fiore@0: }
fiore@0:
fiore@5:
fiore@0: @Override
fiore@0: public void draw(Graphics2D g2) {
fiore@0: Stroke oldStroke = g2.getStroke();
fiore@0: g2.setStroke(getStyle().getStroke());
fiore@5:
fiore@0: /* straight line */
fiore@0: if(points.isEmpty()){
fiore@0: Line2D line = getSegment(getNodeAt(0),getNodeAt(1));
fiore@0: g2.draw(line);
fiore@0:
fiore@0: /* draw arrow heads if any */
fiore@0: ArrowHead h = currentHeads.get(getNodeAt(0));
fiore@0: if( h != null && h != ArrowHead.TAIL){
fiore@0: Line2D revLine = getSegment(getNodeAt(1),getNodeAt(0));
fiore@0: h.draw(g2, revLine.getP1(), revLine.getP2());
fiore@0: }
fiore@0: h = currentHeads.get(getNodeAt(1));
fiore@0: if( h != null && h != ArrowHead.TAIL){
fiore@0: h.draw(g2, line.getP1(), line.getP2());
fiore@0: }
fiore@0:
fiore@0: /* draw labels if any */
fiore@0: String label;
fiore@0: if((label = getEndLabel(getNodeAt(0))) != null){
fiore@0: EdgeDrawSupport.drawString(g2, line.getP2(), line.getP1(), currentHeads.get(getNodeAt(0)), label, false);
fiore@0: }
fiore@0: if((label = getEndLabel(getNodeAt(1))) != null){
fiore@0: EdgeDrawSupport.drawString(g2, line.getP1(), line.getP2(), currentHeads.get(getNodeAt(1)), label, false);
fiore@0: }
fiore@0:
fiore@0: /* draw name if any */
fiore@0: if(!getName().isEmpty()){
fiore@0: EdgeDrawSupport.drawString(g2, line.getP2(), line.getP1(), null, getName(), true);
fiore@0: }
fiore@0: if(!"".equals(getNotes()))
fiore@0: EdgeDrawSupport.drawMarker(g2,line.getP1(),line.getP2());
fiore@0: }else{
fiore@0: /* edge with inner points: it can be a multiended(eventually bended) edge or a straight bended edge */
fiore@0:
fiore@0: /* arrow and labels are drawn in the same way in either case */
fiore@0: for(InnerPoint p : points){
fiore@0: for(GraphElement ge : p.getNeighbours()){
fiore@0: g2.draw(getSegment(p,ge));
fiore@0: if(ge instanceof Node){ // this is the inner point which is connected to a Node
fiore@0: /* draw arrow if any */
fiore@0: ArrowHead h = currentHeads.get((Node)ge);
fiore@0: if(h != null && h != ArrowHead.TAIL){
fiore@0: Line2D line = getSegment(p,ge);
fiore@0: h.draw(g2, line.getP1() , line.getP2());
fiore@0: }
fiore@0:
fiore@0: /* draw label if any */
fiore@0: String label = getEndLabel((Node)ge);
fiore@0: if(label != null){
fiore@0: Line2D line = getSegment(p,ge);
fiore@0: EdgeDrawSupport.drawString(g2, line.getP1(), line.getP2(), currentHeads.get((Node)ge), label, false);
fiore@0: }
fiore@0: }
fiore@0: }
fiore@0: p.draw(g2);
fiore@0: }
fiore@0: /* name is drawn differently :
fiore@0: * for multiended edges name is drawn on the master inner point
fiore@0: * for two ends bended name is drawn in (about) the middle point of the edge
fiore@0: */
fiore@0:
fiore@0: if(masterInnerPoint != null){/* multiended edge */
fiore@0: Rectangle2D bounds = masterInnerPoint.getBounds();
fiore@0: Point2D p = new Point2D.Double(bounds.getCenterX() - 1,bounds.getCenterY());
fiore@0: Point2D q = new Point2D.Double(bounds.getCenterX() + 1,bounds.getCenterY());
fiore@0: if(!getName().isEmpty())
fiore@0: EdgeDrawSupport.drawString(g2, p, q, null, getName(), true);
fiore@0: if(!"".equals(getNotes()))
fiore@0: EdgeDrawSupport.drawMarker(g2,p,q);
fiore@0: }else{
fiore@0: /* straight edge which has been bended */
fiore@0: GraphElement ge1 = getNodeAt(0);
fiore@0: GraphElement ge2 = getNodeAt(1);
fiore@0: InnerPoint c1 = null;
fiore@0: InnerPoint c2 = null;
fiore@0:
fiore@0: for(InnerPoint innp : points){
fiore@0: if(innp.getNeighbours().contains(ge1)){
fiore@0: c1 = innp;
fiore@0: }
fiore@0: if(innp.getNeighbours().contains(ge2)){
fiore@0: c2 = innp;
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: /* draw name if any */
fiore@0: if(!getName().isEmpty()){
fiore@0: /* we only have two nodes but the edge has been bended */
fiore@0: while((c1 != c2)&&(!c2.getNeighbours().contains(c1))){
fiore@0: if(c1.getNeighbours().get(0) == ge1){
fiore@0: ge1 = c1;
fiore@0: c1 = (InnerPoint)c1.getNeighbours().get(1);
fiore@0: }
fiore@0: else{
fiore@0: ge1 = c1;
fiore@0: c1 = (InnerPoint)c1.getNeighbours().get(0);
fiore@0: }
fiore@0: if(c2.getNeighbours().get(0) == ge2){
fiore@0: ge2 = c2;
fiore@0: c2 = (InnerPoint)c2.getNeighbours().get(1);
fiore@0: }
fiore@0: else{
fiore@0: ge2 = c2;
fiore@0: c2 = (InnerPoint)c2.getNeighbours().get(0);
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: Point2D p = new Point2D.Double();
fiore@0: Point2D q = new Point2D.Double();
fiore@0: if(c1 == c2){
fiore@0: Rectangle2D bounds = c1.getBounds();
fiore@0: p.setLocation( bounds.getCenterX() - 1,bounds.getCenterY());
fiore@0: q.setLocation( bounds.getCenterX() + 1,bounds.getCenterY());
fiore@0: }else{
fiore@0: Rectangle2D bounds = c1.getBounds();
fiore@0: p.setLocation( bounds.getCenterX(),bounds.getCenterY());
fiore@0: bounds = c2.getBounds();
fiore@0: q.setLocation(bounds.getCenterX(),bounds.getCenterY());
fiore@0:
fiore@0: }
fiore@0: if(!getName().isEmpty())
fiore@0: EdgeDrawSupport.drawString(g2, p, q, null, getName(), true);
fiore@0: if(!"".equals(getNotes()))
fiore@0: EdgeDrawSupport.drawMarker(g2,p,q);
fiore@0: }
fiore@0: }
fiore@0: }
fiore@0: g2.setStroke(oldStroke);
fiore@0: }
fiore@0:
fiore@0: public Rectangle2D getBounds() {
fiore@0: if(points.isEmpty()){
fiore@0: return getSegment(getNodeAt(0), getNodeAt(1)).getBounds2D();
fiore@0: }else{
fiore@0: Rectangle2D bounds = points.get(0).getBounds();
fiore@0: for(InnerPoint p : points){
fiore@0: for(GraphElement ge : p.getNeighbours())
fiore@0: bounds.add(getSegment(p,ge).getBounds2D());
fiore@0: }
fiore@0: return bounds;
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: public ArrowHead[] getHeads() {
fiore@0: return heads;
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public InputStream getSound(){
fiore@0: switch(getStyle()){
fiore@0: case Dashed : return dashedSound;
fiore@0: case Dotted : return dottedSound;
fiore@0: default : return straightSound;
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@3: public void setEndDescription(DiagramNode diagramNode, int index, Object source){
fiore@0: Node n = (Node)diagramNode;
fiore@0: if(index == NO_END_DESCRIPTION_INDEX){
fiore@0: currentHeads.remove(n);
fiore@3: super.setEndDescription(n, index,source);
fiore@0: }else{
fiore@0: ArrowHead h = heads[index];
fiore@0: currentHeads.put(n, h);
fiore@3: super.setEndDescription(n, index,source);
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void encode(Document doc, Element parent, List nodes){
fiore@0: super.encode(doc, parent, nodes);
fiore@0: /* add the head attribute to the NODE tag */
fiore@0: NodeList nodeTagList = parent.getElementsByTagName(PersistenceManager.NODE);
fiore@0: for(int i = 0 ; i< nodeTagList.getLength(); i++){
fiore@0: Element nodeTag = (Element)nodeTagList.item(i);
fiore@0: String nodeIdAsString = nodeTag.getAttribute(PersistenceManager.ID);
fiore@0: long nodeId = Long.parseLong(nodeIdAsString);
fiore@0: Node node = null;
fiore@0: /* find the node with the id of the tag */
fiore@0: for(Node n : nodes)
fiore@0: if(n.getId() == nodeId){
fiore@0: node = n;
fiore@0: break;
fiore@0: }
fiore@0: String head = (currentHeads.get(node) == null) ? "" : currentHeads.get(node).toString();
fiore@0: nodeTag.setAttribute(SimpleShapePrototypePersistenceDelegate.HEAD, head );
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void decode(Document doc, Element edgeTag, Map nodesId) throws IOException{
fiore@0: super.decode(doc, edgeTag, nodesId);
fiore@0: NodeList nodeList = edgeTag.getElementsByTagName(PersistenceManager.NODE);
fiore@0: for(int i=0; i currentHeads;
fiore@0: private static InputStream straightSound;
fiore@0: private static InputStream dottedSound;
fiore@0: private static InputStream dashedSound;
fiore@0:
fiore@0: static{
fiore@0: Class c = SimpleShapeEdge.class;
fiore@0: straightSound = c.getResourceAsStream("audio/straightLine.mp3");
fiore@0: dottedSound = c.getResourceAsStream("audio/dashedLine.mp3");
fiore@0: dashedSound = c.getResourceAsStream("audio/dottedLine.mp3");
fiore@0: SoundFactory.getInstance().loadSound(straightSound);
fiore@0: SoundFactory.getInstance().loadSound(dottedSound);
fiore@0: SoundFactory.getInstance().loadSound(dashedSound);
fiore@0: }
fiore@0: }