Mercurial > hg > ccmieditor
view java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java @ 8:ea7885bd9bff tip
fixed bug : render solid line as dotted/dashed when moving the stylus from dotted/dashed to solid
author | ccmi-guest |
---|---|
date | Thu, 03 Jul 2014 16:12:20 +0100 |
parents | 1c5af356bb99 |
children |
line wrap: on
line source
/* CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com) Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.ccmi.gui; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramEdge; import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode; import uk.ac.qmul.eecs.ccmi.diagrammodel.ElementChangedEvent; import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties; import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties.Modifiers; import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager; /** * An node in a graph. {@code Node} objects are used in a {@code GraphPanel} to render diagram nodes visually. * {@code Node} objects are used in the tree representation of the diagram as well, as they're * subclasses of {@link DiagramNode} * */ @SuppressWarnings("serial") public abstract class Node extends DiagramNode implements GraphElement{ /** * Constructor to be called by sub classes * * @param type the type of the new node. All nodes with this type will be * put under the same tree node in the tree representation * @param properties the properties of this node */ public Node(String type, NodeProperties properties){ super(type,properties); attachedEdges = new ArrayList<Edge>(); } /* --- DiagramNode abstract methods implementation --- */ @Override public int getEdgesNum(){ return attachedEdges.size(); } @Override public Edge getEdgeAt(int index){ return attachedEdges.get(index); } @Override public boolean addEdge(DiagramEdge e){ return attachedEdges.add((Edge)e); } @Override public boolean removeEdge(DiagramEdge e){ return attachedEdges.remove((Edge)e); } @Override public Node getExternalNode(){ return null; } @Override public void setExternalNode(DiagramNode node){ throw new UnsupportedOperationException(); } @Override public Node getInternalNodeAt(int i){ throw new UnsupportedOperationException(); } @Override public int getInternalNodesNum(){ return 0; } @Override public void addInternalNode(DiagramNode node){ throw new UnsupportedOperationException(); } @Override public void removeInternalNode(DiagramNode node){ throw new UnsupportedOperationException(); } @Override public void stopMove(Object source){ notifyChange(new ElementChangedEvent(this,this,"stop_move",source)); /* edges can change as a result of nodes motion thus we call the method for all the edges * of the node regardless what the mouse point is */ for(int i = 0; i < getEdgesNum();i++){ getEdgeAt(i).stopMove(source); } } @Override public void translate( Point2D p , double dx, double dy, Object source){ translateImplementation( p, dx, dy); for(int i=0; i< getInternalNodesNum();i++){ getInternalNodeAt(i).translate(p, dx, dy,source); } notifyChange(new ElementChangedEvent(this, this, "translate", source)); } @Override protected void setNotes(String notes,Object source){ this.notes = notes; notifyChange(new ElementChangedEvent(this,this,"notes",source)); } /** * The actual implementation of {@code translate()}. The {@code translate} method * when called will, in turn, call this method, and then call all the registered * change listeners in order to notify them that the node has been translated. * * @param p the point we are translating from * @param dx the amount to translate in the x-direction * @param dy the amount to translate in the y-direction */ protected abstract void translateImplementation(Point2D p , double dx, double dy); /** * Tests whether the node contains a point. * @param aPoint the point to test * @return true if this node contains aPoint */ public abstract boolean contains(Point2D aPoint); @Override public abstract Rectangle2D getBounds(); @Override public void startMove(Point2D p,Object source){ /* useless, here just to comply with the GraphElement interface */ } @Override public abstract Point2D getConnectionPoint(Direction d); @Override public void draw(Graphics2D g2){ if(!"".equals(getNotes())){ Rectangle2D bounds = getBounds(); Color oldColor = g2.getColor(); g2.setColor(GraphPanel.GRABBER_COLOR); g2.fill(new Rectangle2D.Double(bounds.getX() - MARKER_SIZE / 2, bounds.getY() - MARKER_SIZE / 2, MARKER_SIZE, MARKER_SIZE)); g2.setColor(oldColor); } } /** * Returns the geometric shape of this node * * @return the shape of this node */ public abstract Shape getShape(); /** * Encodes the internal data of this node (position, name, properties, modifiers) in XML format. * * The saved data can be retrieved and set back via {@code decode}. * * @param doc An XMl document * @param parent the parent XML tag this node tag will be nested in */ public void encode(Document doc, Element parent){ parent.setAttribute(PersistenceManager.NAME,getName()); Element positionTag = doc.createElement(PersistenceManager.POSITION); Rectangle2D bounds = getBounds(); positionTag.setAttribute(PersistenceManager.X, String.valueOf(bounds.getX())); positionTag.setAttribute(PersistenceManager.Y, String.valueOf(bounds.getY())); parent.appendChild(positionTag); if(getProperties().isEmpty()) return; Element propertiesTag = doc.createElement(PersistenceManager.PROPERTIES); parent.appendChild(propertiesTag); for(String type : getProperties().getTypes()){ List<String> values = getProperties().getValues(type); if(values.isEmpty()) continue; Element propertyTag = doc.createElement(PersistenceManager.PROPERTY); propertiesTag.appendChild(propertyTag); Element typeTag = doc.createElement(PersistenceManager.TYPE); typeTag.appendChild(doc.createTextNode(type)); propertyTag.appendChild(typeTag); int index = 0; for(String value : values){ Element elementTag = doc.createElement(PersistenceManager.ELEMENT); propertyTag.appendChild(elementTag); Element valueTag = doc.createElement(PersistenceManager.VALUE); valueTag.appendChild(doc.createTextNode(value)); elementTag.appendChild(valueTag); Set<Integer> modifierIndexes = getProperties().getModifiers(type).getIndexes(index); if(!modifierIndexes.isEmpty()){ Element modifiersTag = doc.createElement(PersistenceManager.MODIFIERS); StringBuilder builder = new StringBuilder(); for(Integer i : modifierIndexes ) builder.append(i).append(' '); builder.deleteCharAt(builder.length()-1);//remove last space modifiersTag.appendChild(doc.createTextNode(builder.toString())); elementTag.appendChild(modifiersTag); } index++; } } } /** * Sets the internal data of this node (position, name, properties, modifiers) from an XML file * node tag previously encoded via {@code encode} * * @param doc An XMl document * @param nodeTag the XML {@code PersistenceManager.NODE } tag with data for this node * @throws IOException if something goes wrong when reading the document. E.g. when the file is corrupted * * @see uk.ac.qmul.eecs.ccmi.gui.persistence */ public void decode(Document doc, Element nodeTag) throws IOException{ setName(nodeTag.getAttribute(PersistenceManager.NAME),DiagramEventSource.PERS); try{ setId(Integer.parseInt(nodeTag.getAttribute(PersistenceManager.ID))); }catch(NumberFormatException nfe){ throw new IOException(); } if(nodeTag.getElementsByTagName(PersistenceManager.POSITION).item(0) == null) throw new IOException(); Element positionTag = (Element)nodeTag.getElementsByTagName(PersistenceManager.POSITION).item(0); double dx,dy; try{ dx = Double.parseDouble(positionTag.getAttribute(PersistenceManager.X)); dy = Double.parseDouble(positionTag.getAttribute(PersistenceManager.Y)); }catch(NumberFormatException nfe){ throw new IOException(); } Rectangle2D bounds = getBounds(); translate(new Point2D.Double(0,0), dx - bounds.getX(), dy - bounds.getY(),DiagramEventSource.PERS); NodeList propList = nodeTag.getElementsByTagName(PersistenceManager.PROPERTY); NodeProperties properties = getProperties(); for(int j=0; j<propList.getLength();j++){ Element propertyTag = (Element)propList.item(j); if(propertyTag.getElementsByTagName(PersistenceManager.TYPE).item(0) == null) throw new IOException(); Element pTypeTag = (Element)propertyTag.getElementsByTagName(PersistenceManager.TYPE).item(0); String propertyType = pTypeTag.getTextContent(); /* scan all the <Element> of the current <Property>*/ NodeList elemValueList = propertyTag.getElementsByTagName(PersistenceManager.ELEMENT); for(int h=0; h<elemValueList.getLength(); h++){ Element elemTag = (Element)elemValueList.item(h); /* get the <value> */ if(elemTag.getElementsByTagName(PersistenceManager.VALUE).item(0) == null) throw new IOException(); Element valueTag = (Element)elemTag.getElementsByTagName(PersistenceManager.VALUE).item(0); String value = valueTag.getTextContent(); /* <modifiers>. need to go back on the prototypes because the content of <modifier> is a list */ /* of int index pointing to the modifiers type, defined just in the prototypes */ Element prototypesTag = (Element)doc.getElementsByTagName(PersistenceManager.PROTOTYPES).item(0); Modifiers modifiers = null; try { modifiers = properties.getModifiers(propertyType); }catch(IllegalArgumentException iae){ throw new IOException(iae); } if(!modifiers.isNull()){ Element modifiersTag = (Element)((Element)elemValueList.item(h)).getElementsByTagName(PersistenceManager.MODIFIERS).item(0); if(modifiersTag != null){ //else there are no modifiers specified for this property value Set<Integer> indexesToAdd = new LinkedHashSet<Integer>(); String indexesString = modifiersTag.getTextContent(); String[] indexes = indexesString.split(" "); for(String s : indexes){ try{ int index = Integer.parseInt(s); NodeList templatePropList = prototypesTag.getElementsByTagName(PersistenceManager.PROPERTY); String modifiersType = null; /* look at the property prototypes to see which modifier the index is referring to. * * The index is in fact the id attribute of the <Modifier> tag in the prototypes section */ for(int k=0; k<templatePropList.getLength();k++){ Element prototypePropTag = (Element)templatePropList.item(k); Element prototypePropTypeTag = (Element)prototypePropTag.getElementsByTagName(PersistenceManager.TYPE).item(0); if(propertyType.equals(prototypePropTypeTag.getTextContent())){ NodeList proptotypeModifierList = prototypePropTag.getElementsByTagName(PersistenceManager.MODIFIER); for(int m = 0 ; m<proptotypeModifierList.getLength();m++){ if(index == Integer.parseInt(((Element)proptotypeModifierList.item(m)).getAttribute(PersistenceManager.ID))){ Element modifierTypeTag = (Element)((Element)proptotypeModifierList.item(m)).getElementsByTagName(PersistenceManager.TYPE).item(0); modifiersType = modifierTypeTag.getTextContent(); } } } } if(modifiersType == null) // the index must point to a valid modifier's id throw new IOException(); indexesToAdd.add(Integer.valueOf(modifiers.getTypes().indexOf(modifiersType))); }catch(NumberFormatException nfe){ throw new IOException(nfe); } } addProperty(propertyType, value,DiagramEventSource.PERS);//whether propertyType actually exist in the prototypes has been already checked setModifierIndexes(propertyType, h, indexesToAdd,DiagramEventSource.PERS); }else addProperty(propertyType, value,DiagramEventSource.PERS); }else addProperty(propertyType, value,DiagramEventSource.PERS); } } } @SuppressWarnings("unchecked") @Override public Object clone(){ Node clone = (Node)super.clone(); clone.attachedEdges = (ArrayList<Edge>) attachedEdges.clone(); return clone; } /** * An array of references to the edges attached to this node */ protected ArrayList<Edge> attachedEdges; private final int MARKER_SIZE = 7; /** * The shadow color of nodes */ protected static final Color SHADOW_COLOR = Color.LIGHT_GRAY; public static final int SHADOW_GAP = 2; }