annotate java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeEdge.java @ 1:e3935c01cde2 tip

moved license of PdPersistenceManager to the beginning of the file
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 19:52:03 +0100
parents 78b7fc5391a2
children
rev   line source
f@0 1 /*
f@0 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
f@0 3
f@0 4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
f@0 5
f@0 6 This program is free software: you can redistribute it and/or modify
f@0 7 it under the terms of the GNU General Public License as published by
f@0 8 the Free Software Foundation, either version 3 of the License, or
f@0 9 (at your option) any later version.
f@0 10
f@0 11 This program is distributed in the hope that it will be useful,
f@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 14 GNU General Public License for more details.
f@0 15
f@0 16 You should have received a copy of the GNU General Public License
f@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 18 */
f@0 19
f@0 20 package uk.ac.qmul.eecs.ccmi.simpletemplate;
f@0 21
f@0 22 import java.awt.Graphics2D;
f@0 23 import java.awt.Stroke;
f@0 24 import java.awt.geom.Line2D;
f@0 25 import java.awt.geom.Point2D;
f@0 26 import java.awt.geom.Rectangle2D;
f@0 27 import java.io.IOException;
f@0 28 import java.io.InputStream;
f@0 29 import java.util.HashMap;
f@0 30 import java.util.List;
f@0 31 import java.util.Map;
f@0 32
f@0 33 import org.w3c.dom.Document;
f@0 34 import org.w3c.dom.Element;
f@0 35 import org.w3c.dom.NodeList;
f@0 36
f@0 37 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode;
f@0 38 import uk.ac.qmul.eecs.ccmi.gui.DiagramEventSource;
f@0 39 import uk.ac.qmul.eecs.ccmi.gui.Edge;
f@0 40 import uk.ac.qmul.eecs.ccmi.gui.GraphElement;
f@0 41 import uk.ac.qmul.eecs.ccmi.gui.LineStyle;
f@0 42 import uk.ac.qmul.eecs.ccmi.gui.Node;
f@0 43 import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;
f@0 44 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
f@0 45
f@0 46 /**
f@0 47 * An edge rendered as a straight, dotted or dashed line. The edge can have an arrow head
f@0 48 * at each end. Possible arrow heads are :
f@0 49 * <ul>
f@0 50 * <li> Triangle
f@0 51 * <li> Black Triangle
f@0 52 * <li> V
f@0 53 * <li> Half V
f@0 54 * <li> Diamond
f@0 55 * <li> Black Diamond
f@0 56 * <li> Tail
f@0 57 * </ul>
f@0 58 *
f@0 59 */
f@0 60 @SuppressWarnings("serial")
f@0 61 public class SimpleShapeEdge extends Edge {
f@0 62
f@0 63 public SimpleShapeEdge(String type, LineStyle style, ArrowHead[] heads, String[] availableEndDescriptions, int minAttachedNodes, int maxAttachedNodes) {
f@0 64 super(type,availableEndDescriptions,minAttachedNodes,maxAttachedNodes,style);
f@0 65 this.heads = heads;
f@0 66 currentHeads = new HashMap<Node,ArrowHead>();
f@0 67 }
f@0 68
f@0 69 @Override
f@0 70 public boolean removeNode(DiagramNode n, Object source){
f@0 71 currentHeads.remove(n);
f@0 72 return super.removeNode(n,source);
f@0 73 }
f@0 74
f@0 75
f@0 76 @Override
f@0 77 public void draw(Graphics2D g2) {
f@0 78 Stroke oldStroke = g2.getStroke();
f@0 79 g2.setStroke(getStyle().getStroke());
f@0 80
f@0 81 /* straight line */
f@0 82 if(points.isEmpty()){
f@0 83 Line2D line = getSegment(getNodeAt(0),getNodeAt(1));
f@0 84 g2.draw(line);
f@0 85
f@0 86 /* draw arrow heads if any */
f@0 87 ArrowHead h = currentHeads.get(getNodeAt(0));
f@0 88 if( h != null && h != ArrowHead.TAIL){
f@0 89 Line2D revLine = getSegment(getNodeAt(1),getNodeAt(0));
f@0 90 h.draw(g2, revLine.getP1(), revLine.getP2());
f@0 91 }
f@0 92 h = currentHeads.get(getNodeAt(1));
f@0 93 if( h != null && h != ArrowHead.TAIL){
f@0 94 h.draw(g2, line.getP1(), line.getP2());
f@0 95 }
f@0 96
f@0 97 /* draw labels if any */
f@0 98 String label;
f@0 99 if((label = getEndLabel(getNodeAt(0))) != null){
f@0 100 EdgeDrawSupport.drawString(g2, line.getP2(), line.getP1(), currentHeads.get(getNodeAt(0)), label, false);
f@0 101 }
f@0 102 if((label = getEndLabel(getNodeAt(1))) != null){
f@0 103 EdgeDrawSupport.drawString(g2, line.getP1(), line.getP2(), currentHeads.get(getNodeAt(1)), label, false);
f@0 104 }
f@0 105
f@0 106 /* draw name if any */
f@0 107 if(!getName().isEmpty()){
f@0 108 EdgeDrawSupport.drawString(g2, line.getP2(), line.getP1(), null, getName(), true);
f@0 109 }
f@0 110 if(!"".equals(getNotes()))
f@0 111 EdgeDrawSupport.drawMarker(g2,line.getP1(),line.getP2());
f@0 112 }else{
f@0 113 /* edge with inner points: it can be a multiended(eventually bended) edge or a straight bended edge */
f@0 114
f@0 115 /* arrow and labels are drawn in the same way in either case */
f@0 116 for(InnerPoint p : points){
f@0 117 for(GraphElement ge : p.getNeighbours()){
f@0 118 g2.draw(getSegment(p,ge));
f@0 119 if(ge instanceof Node){ // this is the inner point which is connected to a Node
f@0 120 /* draw arrow if any */
f@0 121 ArrowHead h = currentHeads.get((Node)ge);
f@0 122 if(h != null && h != ArrowHead.TAIL){
f@0 123 Line2D line = getSegment(p,ge);
f@0 124 h.draw(g2, line.getP1() , line.getP2());
f@0 125 }
f@0 126
f@0 127 /* draw label if any */
f@0 128 String label = getEndLabel((Node)ge);
f@0 129 if(label != null){
f@0 130 Line2D line = getSegment(p,ge);
f@0 131 EdgeDrawSupport.drawString(g2, line.getP1(), line.getP2(), currentHeads.get((Node)ge), label, false);
f@0 132 }
f@0 133 }
f@0 134 }
f@0 135 p.draw(g2);
f@0 136 }
f@0 137 /* name is drawn differently :
f@0 138 * for multiended edges name is drawn on the master inner point
f@0 139 * for two ends bended name is drawn in (about) the middle point of the edge
f@0 140 */
f@0 141
f@0 142 if(masterInnerPoint != null){/* multiended edge */
f@0 143 Rectangle2D bounds = masterInnerPoint.getBounds();
f@0 144 Point2D p = new Point2D.Double(bounds.getCenterX() - 1,bounds.getCenterY());
f@0 145 Point2D q = new Point2D.Double(bounds.getCenterX() + 1,bounds.getCenterY());
f@0 146 if(!getName().isEmpty())
f@0 147 EdgeDrawSupport.drawString(g2, p, q, null, getName(), true);
f@0 148 if(!"".equals(getNotes()))
f@0 149 EdgeDrawSupport.drawMarker(g2,p,q);
f@0 150 }else{
f@0 151 /* straight edge which has been bended */
f@0 152 GraphElement ge1 = getNodeAt(0);
f@0 153 GraphElement ge2 = getNodeAt(1);
f@0 154 InnerPoint c1 = null;
f@0 155 InnerPoint c2 = null;
f@0 156
f@0 157 for(InnerPoint innp : points){
f@0 158 if(innp.getNeighbours().contains(ge1)){
f@0 159 c1 = innp;
f@0 160 }
f@0 161 if(innp.getNeighbours().contains(ge2)){
f@0 162 c2 = innp;
f@0 163 }
f@0 164 }
f@0 165
f@0 166 /* draw name if any */
f@0 167 if(!getName().isEmpty()){
f@0 168 /* we only have two nodes but the edge has been bended */
f@0 169 while((c1 != c2)&&(!c2.getNeighbours().contains(c1))){
f@0 170 if(c1.getNeighbours().get(0) == ge1){
f@0 171 ge1 = c1;
f@0 172 c1 = (InnerPoint)c1.getNeighbours().get(1);
f@0 173 }
f@0 174 else{
f@0 175 ge1 = c1;
f@0 176 c1 = (InnerPoint)c1.getNeighbours().get(0);
f@0 177 }
f@0 178 if(c2.getNeighbours().get(0) == ge2){
f@0 179 ge2 = c2;
f@0 180 c2 = (InnerPoint)c2.getNeighbours().get(1);
f@0 181 }
f@0 182 else{
f@0 183 ge2 = c2;
f@0 184 c2 = (InnerPoint)c2.getNeighbours().get(0);
f@0 185 }
f@0 186 }
f@0 187
f@0 188 Point2D p = new Point2D.Double();
f@0 189 Point2D q = new Point2D.Double();
f@0 190 if(c1 == c2){
f@0 191 Rectangle2D bounds = c1.getBounds();
f@0 192 p.setLocation( bounds.getCenterX() - 1,bounds.getCenterY());
f@0 193 q.setLocation( bounds.getCenterX() + 1,bounds.getCenterY());
f@0 194 }else{
f@0 195 Rectangle2D bounds = c1.getBounds();
f@0 196 p.setLocation( bounds.getCenterX(),bounds.getCenterY());
f@0 197 bounds = c2.getBounds();
f@0 198 q.setLocation(bounds.getCenterX(),bounds.getCenterY());
f@0 199
f@0 200 }
f@0 201 if(!getName().isEmpty())
f@0 202 EdgeDrawSupport.drawString(g2, p, q, null, getName(), true);
f@0 203 if(!"".equals(getNotes()))
f@0 204 EdgeDrawSupport.drawMarker(g2,p,q);
f@0 205 }
f@0 206 }
f@0 207 }
f@0 208 g2.setStroke(oldStroke);
f@0 209 }
f@0 210
f@0 211 public Rectangle2D getBounds() {
f@0 212 if(points.isEmpty()){
f@0 213 return getSegment(getNodeAt(0), getNodeAt(1)).getBounds2D();
f@0 214 }else{
f@0 215 Rectangle2D bounds = points.get(0).getBounds();
f@0 216 for(InnerPoint p : points){
f@0 217 for(GraphElement ge : p.getNeighbours())
f@0 218 bounds.add(getSegment(p,ge).getBounds2D());
f@0 219 }
f@0 220 return bounds;
f@0 221 }
f@0 222 }
f@0 223
f@0 224 public ArrowHead[] getHeads() {
f@0 225 return heads;
f@0 226 }
f@0 227
f@0 228 @Override
f@0 229 public InputStream getSound(){
f@0 230 switch(getStyle()){
f@0 231 case Dashed : return dashedSound;
f@0 232 case Dotted : return dottedSound;
f@0 233 default : return straightSound;
f@0 234 }
f@0 235 }
f@0 236
f@0 237 @Override
f@0 238 public void setEndDescription(DiagramNode diagramNode, int index, Object source){
f@0 239 Node n = (Node)diagramNode;
f@0 240 if(index == NO_END_DESCRIPTION_INDEX){
f@0 241 currentHeads.remove(n);
f@0 242 super.setEndDescription(n, index,source);
f@0 243 }else{
f@0 244 ArrowHead h = heads[index];
f@0 245 currentHeads.put(n, h);
f@0 246 super.setEndDescription(n, index,source);
f@0 247 }
f@0 248 }
f@0 249
f@0 250 @Override
f@0 251 public void encode(Document doc, Element parent, List<Node> nodes){
f@0 252 super.encode(doc, parent, nodes);
f@0 253 /* add the head attribute to the NODE tag */
f@0 254 NodeList nodeTagList = parent.getElementsByTagName(PersistenceManager.NODE);
f@0 255 for(int i = 0 ; i< nodeTagList.getLength(); i++){
f@0 256 Element nodeTag = (Element)nodeTagList.item(i);
f@0 257 String nodeIdAsString = nodeTag.getAttribute(PersistenceManager.ID);
f@0 258 long nodeId = Long.parseLong(nodeIdAsString);
f@0 259 Node node = null;
f@0 260 /* find the node with the id of the tag */
f@0 261 for(Node n : nodes)
f@0 262 if(n.getId() == nodeId){
f@0 263 node = n;
f@0 264 break;
f@0 265 }
f@0 266 String head = (currentHeads.get(node) == null) ? "" : currentHeads.get(node).toString();
f@0 267 nodeTag.setAttribute(SimpleShapePrototypePersistenceDelegate.HEAD, head );
f@0 268 }
f@0 269 }
f@0 270
f@0 271 @Override
f@0 272 public void decode(Document doc, Element edgeTag, Map<String,Node> nodesId) throws IOException{
f@0 273 super.decode(doc, edgeTag, nodesId);
f@0 274 NodeList nodeList = edgeTag.getElementsByTagName(PersistenceManager.NODE);
f@0 275 for(int i=0; i<nodeList.getLength(); i++){
f@0 276 Element nodeTag = (Element)nodeList.item(i);
f@0 277 String id = nodeTag.getAttribute(PersistenceManager.ID);
f@0 278 if(id.isEmpty())
f@0 279 throw new IOException();
f@0 280 String head = nodeTag.getAttribute(SimpleShapePrototypePersistenceDelegate.HEAD);
f@0 281 if(!head.isEmpty()){
f@0 282 ArrowHead headShape = null;
f@0 283 try{
f@0 284 headShape = ArrowHead.getArrowHeadFromString(head);
f@0 285 }catch(IOException e){
f@0 286 throw e;
f@0 287 }
f@0 288 currentHeads.put(nodesId.get(id), headShape);
f@0 289 int headDescriptionIndex = Edge.NO_END_DESCRIPTION_INDEX;
f@0 290 for(int j=0; j<heads.length;j++){
f@0 291 if(heads[j].equals(headShape)){
f@0 292 headDescriptionIndex = j;
f@0 293 break;
f@0 294 }
f@0 295 }
f@0 296 setEndDescription(nodesId.get(id),headDescriptionIndex,DiagramEventSource.PERS);
f@0 297 }
f@0 298 }
f@0 299 }
f@0 300
f@0 301 @Override
f@0 302 public Object clone(){
f@0 303 return new SimpleShapeEdge(getType(), getStyle(), heads, getAvailableEndDescriptions(), getMinAttachedNodes(), getMaxAttachedNodes() );
f@0 304 }
f@0 305
f@0 306
f@0 307
f@0 308 private ArrowHead[] heads;
f@0 309 private Map<Node,ArrowHead> currentHeads;
f@0 310 private static InputStream straightSound;
f@0 311 private static InputStream dottedSound;
f@0 312 private static InputStream dashedSound;
f@0 313
f@0 314 static{
f@0 315 Class<SimpleShapeEdge> c = SimpleShapeEdge.class;
f@0 316 straightSound = c.getResourceAsStream("audio/straightLine.mp3");
f@0 317 dottedSound = c.getResourceAsStream("audio/dashedLine.mp3");
f@0 318 dashedSound = c.getResourceAsStream("audio/dottedLine.mp3");
f@0 319 SoundFactory.getInstance().loadSound(straightSound);
f@0 320 SoundFactory.getInstance().loadSound(dottedSound);
f@0 321 SoundFactory.getInstance().loadSound(dashedSound);
f@0 322 }
f@0 323 }