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: * 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: }