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