changeset 5:d66dd5880081

Added support for Falcon Haptic device and Tablet/Mouse as haptic device
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Tue, 10 Jul 2012 22:39:37 +0100
parents 2c67ac862920
children 1c5af356bb99
files java/src/uk/ac/qmul/eecs/ccmi/checkboxtree/CheckBoxTree.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/CollectionListener.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramEdge.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramModel.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramNode.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/ElementChangedEvent.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/MalformedEdgeException.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/NodeProperties.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TreeModel.java java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TypeMutableTreeNode.java java/src/uk/ac/qmul/eecs/ccmi/gui/CCmIPopupMenu.java java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramModelUpdater.java java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramPanel.java java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramTree.java java/src/uk/ac/qmul/eecs/ccmi/gui/Direction.java java/src/uk/ac/qmul/eecs/ccmi/gui/Edge.java java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.java java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.properties java/src/uk/ac/qmul/eecs/ccmi/gui/EditorTabbedPane.java java/src/uk/ac/qmul/eecs/ccmi/gui/ExtensionFilter.java java/src/uk/ac/qmul/eecs/ccmi/gui/FileService.java java/src/uk/ac/qmul/eecs/ccmi/gui/Finder.java java/src/uk/ac/qmul/eecs/ccmi/gui/GraphElement.java java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java java/src/uk/ac/qmul/eecs/ccmi/gui/GraphToolbar.java java/src/uk/ac/qmul/eecs/ccmi/gui/HapticKindle.java java/src/uk/ac/qmul/eecs/ccmi/gui/LineStyle.java java/src/uk/ac/qmul/eecs/ccmi/gui/Lock.java java/src/uk/ac/qmul/eecs/ccmi/gui/LoopComboBox.java java/src/uk/ac/qmul/eecs/ccmi/gui/ModifierEditorDialog.java java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyEditorDialog.java java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyTableModel.java java/src/uk/ac/qmul/eecs/ccmi/gui/ResourceFactory.java java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechMenuFactory.java java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechOptionPane.java java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechSummaryPane.java java/src/uk/ac/qmul/eecs/ccmi/gui/TemplateEditor.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessFilter.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanel.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanelEditor.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessTextPane.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/BroadcastFilter.java java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/DisplayFilter.java java/src/uk/ac/qmul/eecs/ccmi/gui/filechooser/SpeechFileChooser.java java/src/uk/ac/qmul/eecs/ccmi/gui/persistence/PersistenceManager.java java/src/uk/ac/qmul/eecs/ccmi/haptics/DummyHaptics.java java/src/uk/ac/qmul/eecs/ccmi/haptics/Edge.java java/src/uk/ac/qmul/eecs/ccmi/haptics/Empties.java java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/FreeImage.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/H3DUtil_vc9.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/HAPI_vc9.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/freeglut.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/pthreadVC2.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListSupport.java java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListener.java java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread.java java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.java java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticsFactory.java java/src/uk/ac/qmul/eecs/ccmi/haptics/MouseHaptics.java java/src/uk/ac/qmul/eecs/ccmi/haptics/Node.java java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.dll java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.java java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.properties java/src/uk/ac/qmul/eecs/ccmi/network/Command.java java/src/uk/ac/qmul/eecs/ccmi/network/LockMessage.java java/src/uk/ac/qmul/eecs/ccmi/network/NetDiagram.java java/src/uk/ac/qmul/eecs/ccmi/network/ProtocolFactory.java java/src/uk/ac/qmul/eecs/ccmi/network/Server.java java/src/uk/ac/qmul/eecs/ccmi/network/ServerConnectionManager.java java/src/uk/ac/qmul/eecs/ccmi/network/ServerLockManager.java java/src/uk/ac/qmul/eecs/ccmi/network/ServerNotRunningException.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/EdgeDrawSupport.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/ModifierView.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/MultiLineString.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/PropertyView.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeEdge.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeNode.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SpeechWizardDialog.java java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/Wizard.java java/src/uk/ac/qmul/eecs/ccmi/speech/BeadsAudioPlayer.java java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.java java/src/uk/ac/qmul/eecs/ccmi/utils/CharEscaper.java java/src/uk/ac/qmul/eecs/ccmi/utils/ExceptionHandler.java java/src/uk/ac/qmul/eecs/ccmi/utils/GridBagUtilities.java java/src/uk/ac/qmul/eecs/ccmi/utils/InteractionLog.java java/src/uk/ac/qmul/eecs/ccmi/utils/Pair.java java/src/uk/ac/qmul/eecs/ccmi/utils/PreferencesService.java java/src/uk/ac/qmul/eecs/ccmi/utils/ResourceFileWriter.java java/src/uk/ac/qmul/eecs/ccmi/utils/Validator.java native/Falcon/CollectionsManager.cpp native/Falcon/CollectionsManager.h native/Falcon/FalconHaptics.vcproj native/Falcon/HapticManager.cpp native/Falcon/HapticManager.h native/Falcon/uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.cpp native/Falcon/uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.h native/Falcon/utils.cpp native/Falcon/utils.h native/PhantomOmni/CollectionsManager.h native/PhantomOmni/HapticManager.cpp native/PhantomOmni/Haptics.vcproj native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_Haptics.cpp native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_Haptics.h native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.cpp native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.h
diffstat 111 files changed, 5134 insertions(+), 1474 deletions(-) [+]
line wrap: on
line diff
--- a/java/src/uk/ac/qmul/eecs/ccmi/checkboxtree/CheckBoxTree.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/checkboxtree/CheckBoxTree.java	Tue Jul 10 22:39:37 2012 +0100
@@ -114,7 +114,7 @@
 	
 	/**
 	 * Returns a reference to the properties holding which nodes of the tree are currently checked.  
-	 * @return the properties or {@value null} if the constructor with no properties was used. 
+	 * @return the properties or {@code null} if the constructor with no properties was used. 
 	 */
 	public SetProperties getProperties(){
 		return properties;
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/CollectionListener.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/CollectionListener.java	Tue Jul 10 22:39:37 2012 +0100
@@ -57,7 +57,7 @@
 	 *  a {@code PropertyChangeArgs} object to retrieve the property from the node. {@code getOldValue()} will return in this 
 	 *  case a String concatenation of all the modifier that were set before the change for this property.
 	 *  <li>{@code arrowHead} : when the arrow head of an edge is changed.
-	 *  <li>{@code endLabel} :
+	 *  <li>{@code endLabel} : when the label of an edge is changed.
 	 *  </ul>
 	 * @param e an object representing the change event
 	 */
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramEdge.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramEdge.java	Tue Jul 10 22:39:37 2012 +0100
@@ -68,6 +68,7 @@
 	 * 
 	 * @param n the node,at whose end the label is located 
 	 * @param label the label
+	 * @param source the source of the action that triggered this method 
 	 */
 	public void setEndLabel(DiagramNode n, String label, Object source){
 		if(label == null)
@@ -80,7 +81,9 @@
 	 * Returns the end label related to a node. On a graphical representation of the diagram 
 	 * the label would be put in proximity of the node.
 	 * 
-	 * @param n the node,at whose end the label is located 
+	 * @param n the node, at whose end the label is located 
+	 * 
+	 * @return the label at the specified end 
 	 */
 	public String getEndLabel(DiagramNode n){
 		String s = endLabels.get(n);
@@ -107,6 +110,7 @@
 	 * edge end description will be set with the string at that position in the array.
 	 * if index is equal to NO_END_DESCRIPTION_INDEX, then the description will be set 
 	 * as the empty string.
+	 * @param source the source of the action that triggered this method 
 	 * 
 	 */
 	public void setEndDescription(DiagramNode n, int index, Object source){
@@ -145,6 +149,7 @@
 	 * Removes a node from this edge. On a graphical representation of the diagram 
 	 * this would mean that the node is no longer connected to the other nodes via this edge. 
 	 * @param n the node to be removed
+	 * @param source the source of this action
 	 * @return true if the inner collection changed as a result of the call
 	 */
 	public abstract boolean removeNode(DiagramNode n, Object source);
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramModel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramModel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -58,8 +58,10 @@
 	/**
 	 * Create a model instance starting from some nodes and edges prototypes. 
 	 * All subsequently added element must be clones of such prototypes.
-	 * @param nodePrototypes
-	 * @param edgePrototypes
+	 * @param nodePrototypes an array of {@code DiagramNode} prototypes, from which 
+	 * nodes that will be inserted in this model will be cloned
+	 * @param edgePrototypes an array of {@code DiagramEdge} prototypes, from which 
+	 * edges that will be inserted in this model will be cloned
 	 */
 	@SuppressWarnings("serial")
 	public DiagramModel(N [] nodePrototypes, E [] edgePrototypes) {
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramNode.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/DiagramNode.java	Tue Jul 10 22:39:37 2012 +0100
@@ -30,6 +30,13 @@
  */
 @SuppressWarnings("serial")
 public abstract class DiagramNode extends DiagramElement {
+	/**
+	 * 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 DiagramNode(String type, NodeProperties properties){
 		setType(type);
 		this.properties = properties;
@@ -68,6 +75,7 @@
 	/**
 	 * Set the NodeProperties of this node
 	 * @param properties  the properties to set for this node
+	 * @param source the source of the action that triggered this method
 	 */
 	public void setProperties(NodeProperties properties, Object source){
 		this.properties = properties;
@@ -78,6 +86,9 @@
 	 * Add a property to the NodeProperties of this node
 	 * @see NodeProperties#addValue(String, String)
 	 * 
+	 * @param propertyType the type of the property to add 
+	 * @param propertyValue the property to add  
+	 * @param source the source of the action that triggered this method
 	 */
 	public void addProperty(String propertyType, String propertyValue, Object source){
 		getProperties().addValue(propertyType, propertyValue);
@@ -88,6 +99,10 @@
 	/**
 	 * Removes a property from the NodeProperties of this node
 	 * @see NodeProperties#removeValue(String, int)
+	 * 
+	 * @param propertyType the type of the property to add
+	 * @param valueIndex the index of the property to remove 
+	 * @param source the source of the action that triggered this method
 	 */
 	public void removeProperty(String propertyType, int valueIndex, Object source){
 		String oldValue = getProperties().getValues(propertyType).get(valueIndex);
@@ -98,6 +113,11 @@
 	/**
 	 * Set a property on the NodeProperties of this node to a new value
 	 * @see NodeProperties#setValue(String, int, String) 
+	 * 
+	 * @param propertyType the type of the property to add
+	 * @param valueIndex the index of the property to remove 
+	 * @param newValue the new value for this property 
+	 * @param source the source of the action that triggered this method
 	 */
 	public void setProperty(String propertyType, int valueIndex, String newValue, Object source){
 		String oldValue = getProperties().getValues(propertyType).get(valueIndex);
@@ -108,6 +128,8 @@
 	/**
 	 * Removes all the values in the NodeProperties of this node
 	 * @see NodeProperties#clear()
+	 * 
+	 * @param source the source of the action that triggered this method
 	 */
 	public void clearProperties(Object source){
 		getProperties().clear();
@@ -117,6 +139,13 @@
 	/**
 	 * set the modifier indexes in the NodeProperties of this node
 	 * @see Modifiers#setIndexes(int, Set)
+	 * 
+	 * @param propertyType the type of the property to add
+	 * @param propertyValueIndex the index of the property value whose modifiers
+	 *  are to be changed  
+	 * @param modifierIndexes the new modifiers (identified by their index ) 
+	 *  for this property value 
+	 * @param source the source of the action that triggered this method
 	 */
 	public void setModifierIndexes(String propertyType, int propertyValueIndex, Set<Integer> modifierIndexes,Object source){
 		StringBuilder oldIndexes = new StringBuilder();
@@ -171,12 +200,16 @@
 	/**
 	 * add an edge to the attached edges list
 	 * @param e the edge to be added
+	 * 
+	 * @return {@code true} if internal collection of edges changed as a result of the call
 	 */
 	public abstract boolean addEdge(DiagramEdge e);
 
 	/**
 	 * Removes an edge from the attached edges list
-	 * @param e the edge to be removed 
+	 * @param e the edge to be removed
+	 * 
+	 * @return {@code true} if internal collection of edges changed as a result of the call
 	 */
 	public abstract boolean removeEdge(DiagramEdge e);
 
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/ElementChangedEvent.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/ElementChangedEvent.java	Tue Jul 10 22:39:37 2012 +0100
@@ -32,7 +32,7 @@
 	 * Creates a new instance of {@code ElementChangedEvent}
 	 * 
 	 * @param element the element that has been changed 
-	 * @param args 
+	 * @param args the arguments of this change event, if any 
 	 * @param changeType it's a {@code String} identifying the change type. Subclasses of {@code DiagramNode} and
 	 * {@code DiagramEdge} can define their own change events and and make listeners aware of them via their
 	 * {@code notifyChnage()} method. Listeners (defined outside this package as well) can then identify such changes using this string. 
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/MalformedEdgeException.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/MalformedEdgeException.java	Tue Jul 10 22:39:37 2012 +0100
@@ -45,10 +45,10 @@
 public class MalformedEdgeException extends RuntimeException {
 
 	/**
-	 * Creates a new {@code MalformedEdgeException} holding the messsage passed as argument.
+	 * Creates a new {@code MalformedEdgeException} holding the message passed as argument.
 	 * The message can be accessed by calling {@code getMessage()} on this exception. 
 	 * 
-	 * @param message
+	 * @param message the message of this Exception 
 	 */
 	public MalformedEdgeException(String message) {
 		super("Edge inserted into data structure was malformed for this reason:" + message);
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/NodeProperties.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/NodeProperties.java	Tue Jul 10 22:39:37 2012 +0100
@@ -269,7 +269,7 @@
 	/**
 	 * Returns true if there are no values for the specified type in this NodeProperties instance.
 	 * 
-	 * @param propertyType
+	 * @param propertyType the property type to be checked for value presence
 	 * @return true if there are no values for the specified type in this NodeProperties instance
 	 */
 	public boolean typeIsEmpty(String propertyType){
@@ -446,7 +446,7 @@
 		 * its own view, which will then be used by the client itself to visualise the modifier    
 		 * 
 		 * @param modifierType the type of modifier the view is associated with. 
-		 * @param o an object defined by user
+		 * @param view an object defined by user defining how this property looks like
 		 * @see #getView(String)
 		 * @throws IllegalArgumentException if modifierType 
 	     * is not among the ones in the type definition passed as argument to the constructor
@@ -463,8 +463,9 @@
 		 * integer set returned by this method tells the client code to which modifiers the property value at the index 
 		 * passed as argument is assigned. The returned indexes refer to the modifier list returned by {@link #getTypes()}
 		 *    
-		 * @param propertyValueIndex
-		 * @return a set of indexes of the modifier types the property value at the index passed as agument
+		 * @param propertyValueIndex the index of the property value for which the set of modifier indexes 
+		 * is being queried 
+		 * @return a set of indexes of the modifier types the property value at the index passed as argument
 		 * is assigned to
 		 */
 		public Set<Integer> getIndexes(int propertyValueIndex){
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TreeModel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TreeModel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -69,6 +69,7 @@
 	 * 
 	 * @param bookmark a bookmark 
 	 * @param treeNode the tree node to be bookmarked
+	 * @param source the sorce of the action that triggered this method 
 	 * @return previous value associated with specified key, or null if there was no mapping for key.
 	 * @throws IllegalArgumentException if bookmark is null 
 	 */
@@ -91,6 +92,7 @@
 	 * Remove the bookmark from the bookmark internal collection
 	 * 
 	 * @param bookmark the bookmark to remove
+	 * @param source the source of the action that triggered this method 
 	 * @return previous value associated with specified key, or null if there was no mapping for key.
 	 */
 	DiagramTreeNode removeBookmark(String bookmark, Object source);
--- a/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TypeMutableTreeNode.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/diagrammodel/TypeMutableTreeNode.java	Tue Jul 10 22:39:37 2012 +0100
@@ -34,7 +34,7 @@
 	/**
 	 * Returns a prototype diagram element which can be cloned to create other diagram elements
 	 * of this type.
-	 * @return
+	 * @return the prototype diagram element 
 	 */
 	public DiagramElement getPrototype(){
 		return (DiagramElement)prototype.clone();
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/CCmIPopupMenu.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/CCmIPopupMenu.java	Tue Jul 10 22:39:37 2012 +0100
@@ -248,7 +248,7 @@
 	 * @param action the action to log.
 	 * @param args additional arguments to add to the log.
 	 * 
-	 * @see uk.ac.qmul.eecs.ccmi.utils#InteractionLog
+	 * @see uk.ac.qmul.eecs.ccmi.utils.InteractionLog 
 	 */
 	protected void iLog(String action, String args) {
 		InteractionLog.log("GRAPH", action, args);
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramModelUpdater.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramModelUpdater.java	Tue Jul 10 22:39:37 2012 +0100
@@ -254,7 +254,7 @@
 	 * model updater. 
 	 *  
 	 * @param ge the graph element being moved
-	 * @param sourcethe source of the {@code stopMove} action. it can be used by collection listeners.
+	 * @param source the source of the {@code stopMove} action. it can be used by collection listeners.
 	 */
 	public void stopMove(GraphElement ge,DiagramEventSource source);
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramPanel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramPanel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -202,12 +202,12 @@
 	}
 
 	/**
-	 * Sets the backing diagram for this panel. This method is used when a diagram is shared 
+	 * Sets the backing up delegate diagram for this panel. This method is used when a diagram is shared 
 	 * (or reverted). A shared diagram has a different way of updating the   
 	 * The modified status is changed according to
 	 * the modified status of the {@code DiagramModel} internal to the new {@code Diagram}   
 	 * 
-	 * @param diagram
+	 * @param diagram the backing up delegate diagram 
 	 */
 	public void setDiagram(Diagram diagram){
 		/* remove the listener from the old model  */
@@ -267,7 +267,7 @@
 	 * to the {@code TreeModel} or {@code CollectionModel} it contains. To change the {@code modified}
 	 * status of the diagram (and of its models) {@code setModified()} must be used. 
 	 * 
-	 * @return
+	 * @return {@code true} if the diagram is modified, {@code false} otherwise
 	 */
 	public boolean isModified(){
 		return diagram.getCollectionModel().isModified();
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramTree.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/DiagramTree.java	Tue Jul 10 22:39:37 2012 +0100
@@ -98,7 +98,7 @@
 	/**
 	 * @see javax.swing.JTree#setModel(javax.swing.tree.TreeModel)
 	 * 
-	 * @param newModel the new mnodel for this tree
+	 * @param newModel the new model for this tree
 	 */
 	public void setModel(TreeModel<Node,Edge> newModel){
 		DiagramTreeNode selectedTreeNode = (DiagramTreeNode)getSelectionPath().getLastPathComponent();
@@ -392,7 +392,6 @@
 	 * Allows only cursor keys, tab key, delete, and actions (CTRL+something)
 	 * 
 	 * @param e a key event 
-	 *  
 	 */
 	@Override
 	protected void processKeyEvent(KeyEvent e){
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Direction.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Direction.java	Tue Jul 10 22:39:37 2012 +0100
@@ -72,6 +72,8 @@
    /**
       Turns this direction by an angle.
       @param angle the angle in degrees
+      
+      @return a new object representing the turned direction 
    */
    public Direction turn(double angle){
       double a = Math.toRadians(angle);
@@ -84,8 +86,7 @@
       Gets the x-component of this direction
       @return the x-component (between -1 and 1)
    */
-   public double getX()
-   {
+   public double getX() {
       return x;
    }
 
@@ -93,8 +94,7 @@
       Gets the y-component of this direction
       @return the y-component (between -1 and 1)
    */
-   public double getY()
-   {
+   public double getY() {
       return y;
    }
    
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Edge.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Edge.java	Tue Jul 10 22:39:37 2012 +0100
@@ -223,11 +223,11 @@
 	@Override
 	public boolean contains(Point2D aPoint){
 		if(points.isEmpty()){ 
-			return  fatStrokeContains (getSegment(nodes.get(0), nodes.get(1)), aPoint);
+			return  fatStrokeContains (nodes.get(0), nodes.get(1), aPoint);
 		}
 		for(InnerPoint p : points){
 			for(GraphElement ge : p.neighbours){
-				if(fatStrokeContains(getSegment(p,ge),aPoint))
+				if(fatStrokeContains(p,ge,aPoint))
 					return true;
 			}
 		}
@@ -284,9 +284,14 @@
 	}
 
 	/* checks if a point belongs to a shape with a margin of MAX_DIST*/
-	private boolean fatStrokeContains(Shape s, Point2D p){
+	private boolean fatStrokeContains(GraphElement ge1, GraphElement ge2, Point2D p){
 		BasicStroke fatStroke = new BasicStroke((float) (2 * MAX_DIST));
-		Shape fatPath = fatStroke.createStrokedShape(s);
+		Line2D line = new Line2D.Double(
+				ge1.getBounds().getCenterX(),
+				ge1.getBounds().getCenterY(),
+				ge2.getBounds().getCenterX(),
+				ge2.getBounds().getCenterY());
+		Shape fatPath = fatStroke.createStrokedShape(line);
 		return fatPath.contains(p);
 	}
 
@@ -346,8 +351,8 @@
 	 * inner point is created and the line is broken into two sub lines. If the location is an 
 	 * already existing inner point, then the point is translated.
 	 * 
-	 * @param p
-	 * @param source
+	 * @param p the starting point of the bending
+	 * @param source the source of the bending action
 	 */
 	public void bend(Point2D p,Object source) {
 		boolean found = false;
@@ -356,29 +361,47 @@
 			points.add(newInnerPoint);
 			newPointCreated = false;
 		}else if(newPointCreated){
-			/* find the segment where the new point lays */
-			for(ListIterator<InnerPoint> pItr = points.listIterator(); pItr.hasNext() && !found; ){
+			/* find the segment closest to where the new point lays */
+			InnerPoint closestP1 = null;
+			GraphElement closestP2 = null;
+			double minDist = 0;
+			for(ListIterator<InnerPoint> pItr = points.listIterator(); pItr.hasNext(); ){
 				InnerPoint ePoint = pItr.next();
 				for(ListIterator<GraphElement> geItr = ePoint.neighbours.listIterator(); geItr.hasNext() && !found;){
 					/* find the neighbour of the current edge point whose line the new point lays on */
-					GraphElement ge = geItr.next();
-					if(fatStrokeContains(getSegment(ePoint, ge),downPoint)){
-						if(ge instanceof InnerPoint ){
-							/* remove current edge point from the neighbour's neighbours */
-							((InnerPoint)ge).neighbours.remove(ePoint);
-							((InnerPoint)ge).neighbours.add(newInnerPoint);
-						}
-						/*remove old neighbour from edgePoint neighbours */
-						geItr.remove();
-						newInnerPoint.neighbours.add(ePoint);
-						newInnerPoint.neighbours.add(ge);
-						/* add the new node to the list of EdgeNodes of this edge */
-						pItr.add(newInnerPoint);
-						geItr.add(newInnerPoint);
-						found = true;
+					GraphElement next = geItr.next();
+					double dist = Line2D.ptSegDist(
+							ePoint.getBounds().getCenterX(),
+							ePoint.getBounds().getCenterY(),
+							next.getBounds().getCenterX(),
+							next.getBounds().getCenterY(),
+							downPoint.getX(),
+							downPoint.getY()
+							);
+					
+					if(closestP1 == null || dist < minDist){
+						closestP1 = ePoint;
+						closestP2 = next;
+						minDist = dist;
+						continue;
 					}
 				}
 			}
+				
+			if(closestP2 instanceof InnerPoint ){
+				/* remove current edge point from the neighbour's neighbours */
+				((InnerPoint)closestP2).neighbours.remove(closestP1);
+				/* add the new inner point */
+				((InnerPoint)closestP2).neighbours.add(newInnerPoint);
+			}
+			/*remove old neighbour from edge inner point neighbours */
+			closestP1.neighbours.remove(closestP2);
+			newInnerPoint.neighbours.add(closestP1);
+			newInnerPoint.neighbours.add(closestP2);
+			/* add the new node to the list of EdgeNodes of this edge */
+			points.add(newInnerPoint);
+			closestP1.neighbours.add(newInnerPoint);
+			found = true;
 			newPointCreated = false;
 		}
 		newInnerPoint.translate(p, p.getX() - newInnerPoint.getBounds().getCenterX(),
@@ -663,7 +686,7 @@
 	private LineStyle style;
 
 
-	private static final double MAX_DIST = 3;
+	private static final double MAX_DIST = 5;
 	private static final Color POINT_COLOR = Color.GRAY;
 	/**
 	 * The end description for an end that has no hand description set by the user
@@ -754,7 +777,7 @@
 
 		private Rectangle2D bounds;
 		private List<GraphElement> neighbours; 
-		private static final int DIM = 5;
+		private static final int DIM = 7;
 	}
 
 	/**
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.java	Tue Jul 10 22:39:37 2012 +0100
@@ -43,6 +43,7 @@
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.InetSocketAddress;
+import java.net.URL;
 import java.nio.channels.SocketChannel;
 import java.text.MessageFormat;
 import java.text.SimpleDateFormat;
@@ -56,6 +57,7 @@
 import java.util.regex.Pattern;
 
 import javax.imageio.ImageIO;
+import javax.swing.ImageIcon;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JFrame;
 import javax.swing.JMenu;
@@ -106,7 +108,6 @@
 import uk.ac.qmul.eecs.ccmi.utils.ExceptionHandler;
 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
-import uk.ac.qmul.eecs.ccmi.utils.Validator;
 
 /**
  *  The main frame of the editor which contains diagram panes that show graphs and 
@@ -121,7 +122,7 @@
 	 * @param templateFiles an array of template files. New diagrams can be created from template files by clonation 
 	 * @param backupDirPath the path of a folder where all the currently open diagrams will be saved if 
 	 * the haptic device crashes  
-	 * @param templateEditors
+	 * @param templateEditors the template editors for this instance of the program 
 	 */
 	public EditorFrame(Haptics haptics, File[] templateFiles, String backupDirPath, TemplateEditor[] templateEditors){  
 		this.backupDirPath = backupDirPath; 
@@ -135,7 +136,8 @@
 		/* read editor related preferences */
 		preferences = PreferencesService.getInstance();
 
-		setIconImage(new ResourceFactory.ImageFactory().getImage("ccmi_favicon.gif"));
+		URL url = getClass().getResource("ccmi_favicon.gif");
+		setIconImage(new ImageIcon(url).getImage());
 		changeLookAndFeel(preferences.get("laf", null));
 
 		recentFiles = new ArrayList<String>(); 
@@ -625,6 +627,41 @@
 		JMenu preferencesMenu = factory.createMenu("preferences");
 		menuBar.add(preferencesMenu);
 
+		/* show haptic window menu item only unless it's a actual haptic device thread * 
+		 * that has its own window run by a native dll                                 */
+		if(!HapticsFactory.getInstance().isAlive()){
+			preferencesMenu.add(factory.createCheckBoxMenuItem("preferences.show_haptics", new ActionListener(){
+				@Override
+				public void actionPerformed(ActionEvent evt){
+					JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
+					haptics.setVisible(menuItem.isSelected());
+					NarratorFactory.getInstance().speakWholeText(resources.getString(
+							menuItem.isSelected() ? "speech.haptic_window_open" : "speech.haptic_window_close"));
+				}
+			}));
+		}
+		
+		preferencesMenu.add(factory.createMenuItem("preferences.change_haptics", new ActionListener(){
+			@Override
+			public void actionPerformed(ActionEvent evt){
+				String [] hapticDevices = {
+						HapticsFactory.PHANTOM_ID,
+						HapticsFactory.FALCON_ID,
+						HapticsFactory.TABLET_ID};
+				String selection = (String)SpeechOptionPane.showSelectionDialog(
+						EditorFrame.this, 
+						resources.getString("dialog.input.haptics.select"), 
+						hapticDevices, 
+						hapticDevices[0]);
+				if(selection == null)
+					return;
+				preferences.put("haptic_device", selection);
+				SpeechOptionPane.showMessageDialog(EditorFrame.this, 
+						MessageFormat.format(resources.getString("dialog.feedback.haptic_init"),selection),
+						SpeechOptionPane.WARNING_MESSAGE);
+			}
+		}));
+		
 		// awareness menu 
 		JMenu awarenessMenu = factory.createMenu("preferences.awareness");
 		preferencesMenu.add(awarenessMenu);
@@ -633,17 +670,18 @@
 		awarenessMenu.add(factory.createMenuItem("preferences.awareness.broadcast", this, "showAwarenessBroadcastDialog"));
 		awarenessMenu.add(factory.createMenuItem("preferences.awareness.display", this, "showAwarenessDisplayDialog"));
 
-		JMenuItem enableAwarenessVoiceMenuItem = factory.createCheckBoxMenuItem("preferences.awareness.enable_voice",new ActionListener(){
-			@Override
-			public void actionPerformed(ActionEvent evt) {
-				JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
-				NarratorFactory.getInstance().setMuted(!menuItem.isSelected(),Narrator.SECOND_VOICE);
-				PreferencesService.getInstance().put("second_voice_enabled", Boolean.toString(menuItem.isSelected()));
-			}
-		});
+		JMenuItem enableAwarenessVoiceMenuItem = factory.createCheckBoxMenuItem("preferences.awareness.enable_voice",
+				new ActionListener(){
+					@Override
+					public void actionPerformed(ActionEvent evt) {
+						JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
+						NarratorFactory.getInstance().setMuted(!menuItem.isSelected(),Narrator.SECOND_VOICE);
+						PreferencesService.getInstance().put("second_voice_enabled", Boolean.toString(menuItem.isSelected()));
+					}
+				});
 		NarratorFactory.getInstance().setMuted(true,Narrator.FIRST_VOICE);
-		enableAwarenessVoiceMenuItem.setSelected(
-				Boolean.parseBoolean(PreferencesService.getInstance().get("second_voice_enabled", Boolean.toString(true))));
+		enableAwarenessVoiceMenuItem.setSelected(Boolean.parseBoolean(
+				PreferencesService.getInstance().get("second_voice_enabled", Boolean.toString(true))));
 		awarenessMenu.add(enableAwarenessVoiceMenuItem);
 		NarratorFactory.getInstance().setMuted(false,Narrator.FIRST_VOICE);
 
@@ -701,12 +739,32 @@
 				PreferencesService.getInstance().put("use_accessible_filechooser", Boolean.toString(menuItem.isSelected()));
 			}
 		});
+		
+		JMenuItem enableLogMenuItem = factory.createCheckBoxMenuItem("preferences.enable_log", new ActionListener(){
+			@Override
+			public void actionPerformed(ActionEvent evt) {
+				JCheckBoxMenuItem menuItem = (JCheckBoxMenuItem) evt.getSource();
+				PreferencesService preferences = PreferencesService.getInstance();
+				preferences.put("enable_log", Boolean.toString(menuItem.isSelected()));
+				if(menuItem.isSelected()){
+					try{
+						InteractionLog.enable(preferences.get("dir.log", ""));
+					}catch(IOException ioe){
+						SpeechOptionPane.showMessageDialog(EditorFrame.this, resources.getString("dialog.error.log_enable"));
+					}
+				}else{
+					InteractionLog.disable();
+				}
+			}
+		});
 
-		// temporarily mute the narrator to select the menu without triggering a speech 
+		/* temporarily mute the narrator to select the menus without triggering a speech */ 
 		NarratorFactory.getInstance().setMuted(true,Narrator.FIRST_VOICE);
 		fileChooserMenuItem.setSelected(Boolean.parseBoolean(PreferencesService.getInstance().get("use_accessible_filechooser", "true")));
+		enableLogMenuItem.setSelected(Boolean.parseBoolean(PreferencesService.getInstance().get("enable_log", "false")));
 		NarratorFactory.getInstance().setMuted(false,Narrator.FIRST_VOICE);
 		preferencesMenu.add(fileChooserMenuItem);
+		preferencesMenu.add(enableLogMenuItem);
 
 
 		/* --- HELP --- */
@@ -725,7 +783,7 @@
 
 	private void changeLookAndFeel(String lafName){
 		if(lafName == null)
-			lafName = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+			lafName = UIManager.getSystemLookAndFeelClassName();
 		try{
 			UIManager.setLookAndFeel(lafName);
 			SwingUtilities.updateComponentTreeUI(EditorFrame.this);
@@ -835,6 +893,10 @@
 		}
 	}
 
+	/**
+	 * Close a diagram tab. If the diagram has not been saved the user will be 
+	 * asked if they want to save the diagram.
+	 */
 	public void closeFile(){
 		DiagramPanel dPanel = getActiveTab();
 		if(dPanel.isModified()||dPanel.getFilePath() == null){
@@ -862,6 +924,12 @@
 		haptics.removeDiagram(dPanel.getDiagram().getName(), newFocusedTabName);
 	}
 
+	/**
+	 * Saves the currently open tab diagram into a file. If the diagram has no file path associated
+	 * (it has never been saved before), {@link #saveFileAs()} is called.  
+	 * 
+	 * @return {@code true} if the file is successfully saved, or {@code false} otherwise.
+	 */
 	public boolean saveFile(){  
 		DiagramPanel diagramPanel = getActiveTab();
 		if (diagramPanel == null) // no tabs open  
@@ -892,7 +960,10 @@
 	}
 
 	/**
-      Saves the current graph as a new file.
+     * Saves the current diagram as a new file. The user is prompter with a {@code FileChooser}
+     * to chose a file to save the diagram to.
+     * 
+     * @return {@code true} if the file is successfully saved, or {@code false} otherwise.
 	 */
 	public boolean saveFileAs() { 
 		DiagramPanel diagramPanel = getActiveTab();
@@ -983,6 +1054,12 @@
 		System.exit(0);
 	}
 
+	/**
+	 * Changes the selection path of the diagram tree to a specific destination.   
+	 * The user with a selection dialog to choose the destination from the following 
+	 * choices : the root of the diagram, one of the element types, a bookmarked 
+	 * node or a node/edge reference. 
+	 */
 	public void jump(){
 		String[] options = new String[canJumpRef ? 4 : 3];
 		options[0] = resources.getString("options.jump.type");
@@ -1015,6 +1092,10 @@
 		}
 	}
 
+	/**
+	 * Locates on the haptic device the node or edge currently selected on the diagram tree. A command
+	 * is sent to the haptic device which in turns drag the user to the node/edge location.  
+	 */
 	public void locate(){
 		DiagramPanel dPanel = getActiveTab();
 		DiagramTree tree = dPanel.getTree();
@@ -1023,15 +1104,30 @@
 		iLog("locate " +((de instanceof Node)? "node" : "edge"),DiagramElement.toLogString(de));
 	}
 
+	/**
+	 * Selects on the diagram tree the node or edge that's currently being touched by the haptic
+	 * device. 
+	 */
 	public void hHighlight() {
 		getActiveTab().getTree().jumpTo(hapticHighlightDiagramElement);
 		iLog("highlight " +((hapticHighlightDiagramElement instanceof Node)? "node" : "edge"),DiagramElement.toLogString(hapticHighlightDiagramElement));
 	}
 
+	/**
+	 * Sends a command to the haptic device to pick up an element (node or edge) for 
+	 * moving it.
+	 * 
+	 * @param de the diagram element to be picked up 
+	 */
 	public void hPickUp(DiagramElement de) {
 		HapticsFactory.getInstance().pickUp(System.identityHashCode(de));
 	}
 
+	/**
+	 * Prompts the user for an object insertion. The object can be a node, an edge, a property,
+	 * a modifier, an edge label, an edge arrow head. Which object is to be inserted depends on 
+	 * the currently selected tree node on the tree.
+	 */
 	public void insert(){
 		DiagramPanel dPanel = getActiveTab();
 		final DiagramTree tree = dPanel.getTree();
@@ -1079,7 +1175,8 @@
 
 			iLog("open insert property dialog","");
 			final String propertyValue = SpeechOptionPane.showInputDialog(EditorFrame.this, 
-					MessageFormat.format(resources.getString("dialog.input.property.text"),propTypeNode.getName())
+					MessageFormat.format(resources.getString("dialog.input.property.text"),	propTypeNode.getName()),
+					""
 			);
 			if(propertyValue != null){
 				if(!propertyValue.isEmpty()){ //check that the user didn't enter an empty string
@@ -1112,7 +1209,8 @@
 
 				int index = typeNode.getIndex(treeNode);
 				Set<Integer> result = SpeechOptionPane.showModifiersDialog(EditorFrame.this, 
-						MessageFormat.format(resources.getString("dialog.input.check_modifiers"),n.getProperties().getValues(typeNode.getType()).get(index)) , 
+						MessageFormat.format(resources.getString("dialog.input.check_modifiers"),
+								n.getProperties().getValues(typeNode.getType()).get(index)) , 
 						modifiers.getTypes(), 
 						modifiers.getIndexes(index)
 				);
@@ -1151,7 +1249,8 @@
 			if(choice == null){
 				iLog("cancel edge operation selection dialog","");
 				SoundFactory.getInstance().play(SoundEvent.CANCEL);
-				modelUpdater.yieldLock(e, Lock.EDGE_END,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
+				modelUpdater.yieldLock(e, Lock.EDGE_END,
+						new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
 				return;
 			}
 			if(choice.equals(operations[0])){  //operations[0] = edit edge end-label
@@ -1198,10 +1297,16 @@
 					SoundFactory.getInstance().play(SoundEvent.CANCEL);
 				}
 			}
-			modelUpdater.yieldLock(e, Lock.EDGE_END,new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
+			modelUpdater.yieldLock(e, Lock.EDGE_END,
+					new DiagramEventActionSource(DiagramEventSource.TREE,Command.Name.SET_ENDLABEL,e.getId(),e.getName()));
 		}
 	}
 
+	/**
+	 * Prompts the user for an object deletion. The object can be a node, an edge, a property,
+	 * a modifier, an edge label, an edge arrow head. Which object is to be deleted depends on 
+	 * the currently selected tree node on the tree.
+	 */
 	public void delete(){
 		DiagramPanel dPanel = getActiveTab();
 		final DiagramTree tree = dPanel.getTree();
@@ -1274,6 +1379,10 @@
 			throw new IllegalStateException("Cannot delete a tree node of type: "+treeNode.getClass().getName());
 	}
 
+	/**
+	 * Prompts the user for an object deletion. The object can be a node, an edge or a property.
+	 * Which object is to be renamed depends on the currently selected tree node on the tree.
+	 */
 	public void rename(){
 		DiagramPanel dPanel = getActiveTab();
 		DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
@@ -1339,6 +1448,10 @@
 			throw new IllegalStateException("Cannot delete a tree node of type: "+treeNode.getClass().getName());
 	}
 
+	/**
+	 * Prompts the user with a dialog to add or remove bookmarks on a tree node. Which node is to be 
+	 * (un)bookmarked depends on the currently selected tree node on the tree.
+	 */
 	public void editBookmarks(){
 		boolean addBookmark = true;
 		DiagramPanel dPanel = getActiveTab(); 
@@ -1383,7 +1496,7 @@
 			boolean uniqueBookmarkChosen = false;
 			while(!uniqueBookmarkChosen){
 				iLog("open add bookmark dialog","");
-				String bookmark = SpeechOptionPane.showInputDialog(EditorFrame.this, resources.getString("dialog.input.bookmark.text"));
+				String bookmark = SpeechOptionPane.showInputDialog(EditorFrame.this, resources.getString("dialog.input.bookmark.text"),"");
 				if(bookmark != null){
 					if("".equals(bookmark)){
 						iLog("error: entered empty bookmark","");
@@ -1425,6 +1538,10 @@
 		modelUpdater.yieldLock(treeNode, Lock.BOOKMARK, DiagramEventActionSource.NULL);
 	}
 
+	/**
+	 * Prompts the user with a dialog edit notes on a tree node. Which node is to be 
+	 * noted depends on the currently selected tree node on the tree.
+	 */
 	public void editNotes(){
 		DiagramPanel dPanel = getActiveTab();
 		DiagramTreeNode treeNode = (DiagramTreeNode)dPanel.getTree().getLastSelectedPathComponent();
@@ -1457,6 +1574,9 @@
 		modelUpdater.yieldLock(treeNode, Lock.NOTES,DiagramEventActionSource.NULL);
 	}
 
+	/**
+	 * Starts the server on a background thread.
+	 */
 	public void startServer(){
 		iLog("server started","");
 		/* If the awareness filter has not been created yet (by opening the awareness filter dialog) then create it */
@@ -1485,6 +1605,9 @@
 				shareDiagramMenuItem.setEnabled(true);
 	}
 
+	/**
+	 * Stops the running server
+	 */
 	public void stopServer(){
 		/* those network diagrams which are connected to the local server are reverted,       *
 		 * that is the diagram panel is set with the delegate diagram of the network diagram  */
@@ -1512,6 +1635,10 @@
 		iLog("server stopped","");
 	}
 
+	/**
+	 * Makes a diagram shared on the server. When a diagram is shared, remote users can connect to the 
+	 * server and edit it collaboratively with the local and the other connected users. 
+	 */
 	public void shareDiagram(){
 		try{
 			if(server == null)
@@ -1544,14 +1671,12 @@
 		}
 	}
 
-	public void unshareDiagram(){
-		DiagramPanel dPanel = getActiveTab();
-		if(dPanel.getDiagram() instanceof NetDiagram){
-			if(clientConnectionManager != null && clientConnectionManager.isAlive())
-				clientConnectionManager.addRequest(new RmDiagramRequest(dPanel.getDiagram().getName()));
-		}
-	}
-
+	/**
+	 * Prompts the user for a server address and connect to the server. The server is queried for the list 
+	 * of the shared diagrams and the user is prompted  with a selection dialog to chose a diagram to download.
+	 * The diagram is then downloaded and loaded into the diagram editor, ready for shared editing.  
+	 * 
+	 */
 	public void openSharedDiagram(){
 		iLog("open open share diagram dialog","");
 		/* open the window prompting for the server address and make checks on the user input */
@@ -1563,7 +1688,7 @@
 			SoundFactory.getInstance().play(SoundEvent.CANCEL);
 			iLog("cancel open share diagram dialog","");
 			return;
-		}else if(!Validator.validateIPAddr(addr)){
+		}else if(!ProtocolFactory.validateIPAddr(addr)){
 			iLog("error:invalid IP address",addr);
 			SpeechOptionPane.showMessageDialog(this, resources.getString("dialog.share_diagram.wrong_ip"));
 			return;
@@ -1664,6 +1789,10 @@
 		}
 	}
 
+	/**
+	 * Shows the awareness panel, a text pane where all the awareness informations received by the server
+	 * are displayed. 
+	 */
 	public void showAwarenessPanel(){
 		DiagramPanel dPanel = getActiveTab();
 		if(dPanel != null){
@@ -1672,6 +1801,10 @@
 		}
 	}
 
+	/**
+	 * Hides the awareness panel, a text pane where all the awareness informations received by the server
+	 * are displayed. 
+	 */
 	public void hideAwarenessPanel(){
 		DiagramPanel dPanel = getActiveTab();
 		if(dPanel != null){
@@ -1681,6 +1814,10 @@
 		}
 	}
 
+	/**
+	 * Saves all the open diagram which have been modified since the last time they were saved into the 
+	 * <i>backup</i> folder in the ccmi_editor_data directory.  
+	 */
 	public void backupOpenDiagrams(){
 		SimpleDateFormat dateFormat = new SimpleDateFormat("EEE_d_MMM_yyyy_HH_mm_ss");
 		String date = dateFormat.format(new Date());
@@ -1754,6 +1891,8 @@
       @param graph the graph
       @param out the output stream
       @param format the image file format
+      
+      @throws IOException if something goes wrong during the I/O operations 
 	 */
 	public static void saveImage(GraphPanel graph, OutputStream out, String format)
 	throws IOException { 
@@ -1780,6 +1919,12 @@
 		ImageIO.write(image, format, out);
 	}
 
+	/**
+	 * Shows the configuration dialog of the broadcast filter. The broadcast filter affects
+	 * which awareness informations are broadcasted from the server to the other clients. 
+	 * If the local editor is not running the server, changes to the broadcast filter 
+	 * will have no effect.  
+	 */
 	public void showAwarenessBroadcastDialog(){
 		BroadcastFilter filter = BroadcastFilter.getInstance();
 		if(filter == null)
@@ -1791,6 +1936,11 @@
 			filter.showDialog(this);   
 	}
 
+	/**
+	 * Shows the configuration dialog of the display filter. The display filter affects
+	 * which awareness informations are received from the server are actually displayed
+	 * to the user. 
+	 */
 	public void showAwarenessDisplayDialog(){
 		DisplayFilter filter = DisplayFilter.getInstance();
 		if(filter == null)
@@ -1802,6 +1952,10 @@
 			filter.showDialog(this); 
 	}
 
+	/**
+	 * Prompts the user with a dialog to choose he awareness username. The username is used in the 
+	 * awareness information to identify which client is doing the actions that are being notified.  
+	 */
 	public void showAwarnessUsernameDialog(){
 		String oldName = AwarenessMessage.getDefaultUserName();
 		String newName = SpeechOptionPane.showInputDialog(
@@ -1840,10 +1994,16 @@
 		}
 	}
 
+	/**
+	 * Prompts the user with a dialog to choose the port number the local server will listen on. 
+	 */
 	public void showLocalServerPortDialog(){
 		showServerPortDialog("server.local_port","dialog.input.local_server_port","dialog.feedback.local_server_port");
 	}
 
+	/**
+	 * Prompts the user with a dialog to choose the remote server port number to connect to.  
+	 */
 	public void showRemoteServerPortDialog(){
 		showServerPortDialog("server.remote_port","dialog.input.remote_server_port","dialog.feedback.remote_server_port");
 	}
@@ -1896,6 +2056,9 @@
 		);
 	}
 
+	/**
+	 * Displays the Software license in a dialog box.
+	 */
 	public void showLicense() {
 		BufferedReader reader = null; 
 		try{
@@ -1921,6 +2084,15 @@
 		}
 	}
 
+	/**
+	 * Saves the diagram template in the <i>templates</i> folder.
+	 * 
+	 * The template can then be reused to create new diagrams with the same type of nodes and 
+	 * edges.  
+	 * 
+	 * @param diagram the diagram to get the template from
+	 * @throws IOException if something goes wrong with I/O when saving the file
+	 */
 	public void saveDiagramTemplate(Diagram diagram) throws IOException {
 		File file = new File(
 				new StringBuilder(PreferencesService.getInstance().get("home", "."))
@@ -1936,9 +2108,11 @@
 	}
 
 	/**
-   Adds a graph type to the File->New menu.
-   @param resourceName the name of the menu item resource
-   @param graphClass the class object for the graph
+     * Adds a diagram type to the File->New menu.
+     *
+   	 *	@param diagram the diagram whose nodes and edges definition will be used as a template
+   	 * for new diagrams creation.
+     *
 	 */
 	public void addDiagramType(final Diagram diagram){
 		/* this is to prevent the user from creating other diagram prototypes with the same name */
@@ -1982,10 +2156,24 @@
 		preferences.put("recent", recent);   
 	}
 
+	/**
+	 * Returns the currently selected tab's diagram panel.
+	 *  
+	 * @return the currently selected tab's diagram panel or {@code null} 
+	 * if no tab is open.
+	 */
 	public DiagramPanel getActiveTab(){
 		return (DiagramPanel)editorTabbedPane.getSelectedComponent();
 	}
 
+	/**
+	 * Set the variable holding the node or edge that would be highlighted if
+	 * {@link #hHighlight()} is called. The menu item for highlight is also enabled
+	 * if {@code de} is not {@code null}.
+	 * 
+	 * @param de the diagram element to be selected by {@code hHighlight()}
+	 * or {@code null} for no selection. 
+	 */
 	public void selectHapticHighligh(DiagramElement de){
 		hapticHighlightDiagramElement = de;
 		highlightMenuItem.setEnabled(de == null ? false : true);
@@ -1997,7 +2185,7 @@
 		diagramPanel.getTree().addTreeSelectionListener(treeSelectionListener);
 		diagramPanel.setAwarenessPanelListener(awarenessPanelListener);
 		/* update the haptics */
-		haptics.addNewDiagram(diagramPanel.getDiagram().getName(), true);
+		haptics.addNewDiagram(diagramPanel.getDiagram().getName());
 		for(Node n : diagram.getCollectionModel().getNodes())
 			haptics.addNode(n.getBounds().getCenterX(), n.getBounds().getCenterY(), System.identityHashCode(n),null);
 		for(Edge e : diagram.getCollectionModel().getEdges()){
@@ -2133,7 +2321,7 @@
 	private ClientConnectionManager clientConnectionManager;
 	private Haptics haptics;
 	private ResourceBundle resources;
-	private EditorTabbedPane editorTabbedPane;
+	public EditorTabbedPane editorTabbedPane;
 	private FileService.ChooserService fileService;
 	private PreferencesService preferences;
 	private HapticTrigger hapticTrigger;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.properties	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorFrame.properties	Tue Jul 10 22:39:37 2012 +0100
@@ -50,6 +50,7 @@
 dialog.error.save_image=Error: could not save the image to file
 dialog.error.unsupported_image=Error: {0} not supported
 dialog.error.no_connection_to_server=Could not connect to the server
+dialog.error.log_enable=The log could not be enabled
 
 dialog.template_created=Diagram {0} created
 dialog.file_saved=File Saved
@@ -87,6 +88,7 @@
 dialog.input.awerness_username=Enter Awareness User Name 
 dialog.input.local_server_port=Enter local server port number
 dialog.input.remote_server_port=Enter remote server port number
+dialog.input.haptics.select=Select the haptic device you want to use 
 
 dialog.confirm.deletion=Are you sure you want to delete the {0} {1} ?
 dialog.confirm.deletions=Are you sure you want to delete the selected objects ?
@@ -124,12 +126,13 @@
 dialog.share_diagram.enter_address="Enter server address"
 
 dialog.file_chooser.file_type=File Type:
-dialog.file_chooser.file_name=file Name:
+dialog.file_chooser.file_name=File Name:
 
 dialog.feedback.speech_rate=Speech rate set to {0} 
 dialog.feedback.awareness_username=User Name set to {0}
 dialog.feedback.local_server_port=Local server port number set to {0}
 dialog.feedback.remote_server_port=Remote server port number set to {0}
+dialog.feedback.haptic_init={0} selected\u000AYou need to restart the editor in order apply this change 
 
 server.shutdown_msg=request by user
 server.not_running_exc=Server not running
@@ -166,6 +169,9 @@
 speech.awareness_panel.open=Awareness panel open
 speech.awareness_panel.close=Awareness panel close
 
+speech.haptic_window_open=Haptic Window open
+speech.haptic_window_close=Haptic Window closed 
+
 ### TABBED PANE ### 
 tab.new_tab=new {0}
 tab.new_tab_id=new {0} ({1})
@@ -275,6 +281,7 @@
 collab.hide_awareness_panel.text=Hide Awareness Panel
 preferences.text=Preferences
 preferences.mnemonic=P
+preferences.show_haptics.text=Show Haptics Window
 preferences.awareness.text=Awareness
 preferences.awareness.mnemonic=A
 preferences.awareness.username.text=User Name
@@ -282,6 +289,7 @@
 preferences.awareness.display.text=Display Filter
 preferences.awareness.enable_voice.text=Enable Awareness Voice
 preferences.file_chooser.text=Enable accessible file chooser
+preferences.enable_log.text=Enable interaction log
 preferences.sound.text=Sound
 preferences.sound.mnemonic=S
 preferences.sound.mute.text=Mute
@@ -292,6 +300,7 @@
 preferences.server.text=Networking
 preferences.local_server.port.text=Local Server Port
 preferences.remote_server.port.text=Remote Server Port
+preferences.change_haptics.text=Change Haptic Device
 
 
 help.text=Help
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorTabbedPane.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/EditorTabbedPane.java	Tue Jul 10 22:39:37 2012 +0100
@@ -118,10 +118,10 @@
 	
 	/**
 	 * The components in an {@code EditorTabbedPane} are all instances of {@code DiagramPanel}, which in turns
-	 * holds an instance of {@code Diagram}. This method returns the index of the {@code DiagramPanel}
+	 * hold an instance of {@code Diagram}. This method returns the index of the {@code DiagramPanel}
 	 * whose diagram has been saved on the file system at the path specified as argument. 
 	 * 
-	 * @param path
+	 * @param path a path on the file system identifying the diagram
 	 * @return the index of the {@code DiagramPanel} whose diagram has been saved on 
 	 * the file system in {@code path}, or {@code -1} if such diagram doesn't exist
 	 */
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/ExtensionFilter.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/ExtensionFilter.java	Tue Jul 10 22:39:37 2012 +0100
@@ -28,9 +28,7 @@
    A file filter that accepts all files with a given set
    of extensions.
 */
-public class ExtensionFilter 
-   extends FileFilter
-{
+public class ExtensionFilter extends FileFilter {
    /**
       Constructs an extension file filter.
       @param description the description (e.g. "Woozle files")
@@ -46,9 +44,9 @@
    /**
       Constructs an extension file filter.
       @param description the description (e.g. "Woozle files")
+      @param extensions a list of '|'-separated accepted extensions
    */
-   public ExtensionFilter(String description, 
-      String extensions){
+   public ExtensionFilter(String description, String extensions){
       this.description = description; 
       StringTokenizer tokenizer = new StringTokenizer(
          extensions, "|");
@@ -72,10 +70,6 @@
       return description;
    }
    
-   public String[] getExtensions(){
-      return extensions;
-   }
-   
    @Override
    public String toString(){
 	   return description;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/FileService.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/FileService.java	Tue Jul 10 22:39:37 2012 +0100
@@ -39,6 +39,10 @@
 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
 
+/**
+ * A Utility class providing inner classes and interfaces for handling 
+ * files.  
+ */
 public abstract class FileService
 {
 
@@ -51,18 +55,18 @@
 		 * Gets the input stream corresponding to the user selection.
 		 * @return the input stream, or null if the user cancels the file selection task   
 		 */
-		InputStream getInputStream() throws IOException ;
+		InputStream getInputStream();
 		/**
 		 * Gets the name of the file that the user selected.
 		 * @return the file name, or null if the user cancels the file selection task    
 		 */
-		String getName() throws IOException ;
+		String getName();
 
 		/**
 		 * Gets the path of the file that the user selected.
 		 * @return the file path , or null if the user cancels the file selection task     
 		 */
-		String getPath() throws IOException;
+		String getPath();
 
 	}
 
@@ -89,18 +93,35 @@
 	}
 
 	/**
-	 * This class implements a FileService with a JFileChooser
+	 * This class returns a FileService for opening and saving files, with either a JFileChooser or a
+	 * SpeechFileChooser 
 	 */
 	public static class ChooserService 
 	{
+		/**
+		 * Creates a new {@code ChooserService}. 
+		 * 
+		 * @param initialDirectory the directory displayed when the files service is displayed 
+		 */
 		public ChooserService(File initialDirectory){
 			useAccessible = Boolean.parseBoolean(PreferencesService.getInstance().get("use_accessible_filechooser", "true")); 
 			fileChooser = FileChooserFactory.getFileChooser(useAccessible);
 			fileChooser.setCurrentDirectory(initialDirectory);
 		}
 
+		/**
+		 * Returns a {@code FileService.Open} out of a file chosen by the user. The user is prompted 
+		 * with either a normal or an accessible file choser. 
+		 * 
+		 * @param defaultDirectory the directory to open the file chooser, prompted to the user
+		 * @param defaultFile the file selected when the file chooser is prompted to the user
+		 * @param filter an {@code ExtensionFilter} to filter the filed displayed on the file chooser 
+		 * @param frame the frame where the file chooser is displayed 
+		 * @return an {@code FileService.Open} to handle the file selected by the user 
+		 * @throws IOException if the file chosen by the uses doesn't exist.   
+		 */
 		public FileService.Open open(String defaultDirectory, String defaultFile, 
-				ExtensionFilter filter, Frame frame) throws FileNotFoundException {
+				ExtensionFilter filter, Frame frame) throws IOException {
 			checkChangedOption();
 			fileChooser.resetChoosableFileFilters();
 			fileChooser.setFileFilter(filter);
@@ -122,9 +143,20 @@
 			}
 		}
 
-
-		/* If the user cancels the task (presses cancel button or the X at the top left)   *
-		 * the CANCEl sound is played (together with the registered playerListeenr if any) */
+		/**
+		 * Returns a {@code FileService.Save} out of a file chosen by the user. The user is prompted 
+		 * with either a normal or an accessible file chooser. 
+		 * 
+		 * @param defaultDirectory the directory to open the file chooser, prompted to the user
+		 * @param defaultFile the file selected when the file chooser is prompted to the user
+		 * @param filter an {@code ExtensionFilter} to filter the filed displayed on the file chooser 
+		 * @param removeExtension the extension to be removed from the chosen file name. Use {@code null} for removing no extension
+		 * @param addExtension the extension to be added to the chosen file name.
+		 * @param currentTabs an array of already open files names. If the selected file matches any of these, then an
+		 * @return an {@code FileService.Save} to handle the file selected by the user 
+		 * @throws IOException if the file chosen by the uses doesn't exist or has a file name that's already 
+		 * in {@code currentTabs}.  
+		 */
 		public FileService.Save save(String defaultDirectory, String defaultFile, 
 				ExtensionFilter filter, String removeExtension, String addExtension, String[] currentTabs) throws IOException {
 			checkChangedOption();
@@ -150,9 +182,12 @@
 					f = new File(f.getPath() + addExtension);
 				
 				String fileName = getFileNameFromPath(f.getAbsolutePath(),false);
-				for(String tab : currentTabs){
-					if(fileName.equals(tab))
-						throw new IOException(resources.getString("dialog.error.same_file_name"));
+				/* check the name against the names of already open tabs */
+				if(currentTabs != null){
+					for(String tab : currentTabs){
+						if(fileName.equals(tab))
+							throw new IOException(resources.getString("dialog.error.same_file_name"));
+					}
 				}
 				
 				if (!f.exists()) // file doesn't exits return the new SaveImpl with no problems
@@ -176,6 +211,8 @@
 						return new SaveImpl(f);
 				}
 			}
+			/* If the user cancels the task (presses cancel button or the X at the top left)   *
+			 * the CANCEl sound is played (together with the registered playerListeenr if any) */
 			if(useAccessible)
 				SoundFactory.getInstance().play(SoundEvent.CANCEL);
 			/* returned if the user doesn't want to overwrite the file */ 
@@ -197,12 +234,30 @@
 		private boolean useAccessible;
 	}
 
+	/**
+	 * A file service which doesn't show any dialog to let 
+	 * the user choose which file to open or save.  
+	 */
 	public static class DirectService {
-		public Open open(File file) throws IOException{
+		/**
+		 * Creates a {@code FileService.Open} out of the file passed as argument.
+		 * 
+		 * @param file the file to open 
+		 * @return a new {@code FileService.Open} instance
+		 * @throws IOException if {@code file} cannot be found
+		 */
+		public FileService.Open open(File file) throws IOException{
 			return new OpenImpl(file);
 		}
 
-		public Save save(File file) throws IOException{
+		/**
+		 * Creates a {@code FileService.Save} out of the file passed as argument.
+		 * 
+		 * @param file the file to save
+		 * @return a new {@code FileService.Save} instance
+		 * @throws IOException if {@code file} cannot be found
+		 */
+		public FileService.Save save(File file) throws IOException{
 			return new SaveImpl(file);
 		}
 	}
@@ -228,8 +283,7 @@
 		private OutputStream out;
 	}
 
-	private static class OpenImpl implements FileService.Open
-	{
+	private static class OpenImpl implements FileService.Open{
 		public OpenImpl(File f) throws FileNotFoundException{
 			if (f != null){
 				path = f.getPath();
@@ -251,16 +305,16 @@
 	}
 
 	/**
-   Edits the file path so that it ends in the desired 
-   extension.
-   @param original the file to use as a starting point
-   @param toBeRemoved the extension that is to be
-   removed before adding the desired extension. Use
-   null if nothing needs to be removed. 
-   @param desired the desired extension (e.g. ".png"),
-   or a | separated list of extensions
-   @return original if it already has the desired 
-   extension, or a new file with the edited file path
+	 *  Edits the file path so that it ends in the desired 
+	 *  extension.
+	 *  @param original the file to use as a starting point
+	 *  @param toBeRemoved the extension that is to be
+	 *  removed before adding the desired extension. Use
+	 *  null if nothing needs to be removed. 
+	 *  @param desired the desired extension (e.g. ".png"),
+	 *  or a | separated list of extensions
+	 *  @return original if it already has the desired 
+	 *  extension, or a new file with the edited file path
 	 */
 	public static String editExtension(String original,
 			String toBeRemoved, String desired){
@@ -277,6 +331,14 @@
 		return path;      
 	}
 
+	/**
+	 * Returns the single file name from a file path 
+	 * 
+	 * @param path the path to extract the file name from 
+	 * @param keepExtension whether to keep the extension of the file 
+	 *  in the returned string
+	 * @return the name of the file identified by {@code path}
+	 */
 	public static String getFileNameFromPath(String path,boolean keepExtension){
 		int index = path.lastIndexOf(System.getProperty("file.separator"));
 		String name;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Finder.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Finder.java	Tue Jul 10 22:39:37 2012 +0100
@@ -26,29 +26,56 @@
 
 /**
  * 
- * A utility class which provides methods for searching either a node or an edge 
+ * A utility class providing static methods for searching either a node or an edge 
  * in a collection or array.
- *
  */
 public abstract class Finder {
-	public static Node findNode(String nodeClass,Node[] prototypes){
-		for(Node n : prototypes){
-			if(n.getType().equals(nodeClass)){
+	/**
+	 * Finds a node of a type in an array of nodes. The types should be all different
+	 * from each other as only the first node encountered will be returned. If none of the nodes
+	 * as the type passed as argument, {@code null} is returned.   
+	 * 
+	 * @param nodeType the type of the node to find.
+	 * @param nodes the array to search for the node
+	 * @return the first node with type {@code nodeType} or {@code null} if such node
+	 * doesn't exist 
+	 */
+	public static Node findNode(String nodeType,Node[] nodes){
+		for(Node n : nodes){
+			if(n.getType().equals(nodeType)){
 				return n;
 			}
 		}
 		return null;
 	}
 	
-	public static Edge findEdge(String edgeClass,Edge[] prototypes){
-		for(Edge e : prototypes){
-			if(e.getType().equals(edgeClass)){
+	/**
+	 * Finds an edge of a {@code edgeType} type in an array of nodes. The types should be all different
+	 * from each other as only the first edge encountered will be returned. If none of the edges
+	 * as the type passed as argument, {@code null} is returned.   
+	 * 
+	 * @param edgeType the type of the edge to find
+	 * @param edges the array to search for the edge
+	 * @return the first edge with type {@code nodeType} or {@code null} if such edge
+	 * doesn't exist 
+	 */
+	public static Edge findEdge(String edgeType,Edge[] edges){
+		for(Edge e : edges){
+			if(e.getType().equals(edgeType)){
 				return e;
 			}
 		}
 		return null;
 	}
 	
+	/**
+	 * Finds a node with an id in a {@code Collection} of nodes. If none of the nodes
+	 * has the id passed as argument, {@code null} is returned.  
+	 * 
+	 * @param id the id of the node to find 
+	 * @param collection the collection to search for the node 
+	 * @return the node with the specified id, or {@code null} if such node doesn't exist 
+	 */
 	public static Node findNode(Long id, Collection<Node> collection){
 		for(Node n : collection)
 			if(n.getId() == id)
@@ -56,6 +83,14 @@
 		return null;
 	}
 	
+	/**
+	 * Finds a node containing a point {@code p} in a {@code Collection} of nodes. If none of the nodes
+	 * contains the point, {@code null} is returned.  
+	 *
+	 * @param p the point in a graphic environment
+	 * @param collection the collection to search for the node 
+	 * @return the node containing {@code p}, or {@code null} if such node doesn't exist 
+	 */
 	public static Node findNode(Point2D p, Collection<Node> collection){
 		for (Node n : collection)
 			if (n.contains(p)) 
@@ -63,6 +98,29 @@
 		return null;
 	}
 	
+	/**
+	 * Finds an edge with an id in a {@code Collection} of edges. If none of the edges
+	 * has the id passed as argument, {@code null} is returned.  
+	 * 
+	 * @param id the id of the edge to find 
+	 * @param collection the collection to search for the edge 
+	 * @return the edge with the specified id, or {@code null} if such edge doesn't exist 
+	 */
+	public static Edge findEdge(Long id, Collection<Edge> collection){
+		for(Edge e : collection)
+			if(e.getId() == id)
+				return e;
+		return null;
+	}
+	
+	/**
+	 * Finds an edge containing a point {@code p} in a {@code Collection} of edges. If none of the edges
+	 * contains the point, {@code null} is returned.  
+	 * 
+	 * @param p the point in a graphic environment
+	 * @param collection the collection to search for the edge 
+	 * @return the edge containing {@code p}, or {@code null} if such edge doesn't exist 
+	 */
 	public static Edge findEdge(Point2D p, Collection<Edge> collection){
 		for (Edge e : collection)
 			if (e.contains(p)) 
@@ -70,13 +128,14 @@
 		return null;
 	}
 	
-	public static Edge findEdge(Long id, Collection<Edge> collection){
-		for(Edge e : collection)
-			if(e.getId() == id)
-				return e;
-		return null;
-	}
-	
+	/**
+	 * Finds a element (node or edge) with an id in a {@code Collection} of diagram elements. If none of the elements
+	 * has the id passed as argument, {@code null} is returned.  
+	 * 
+	 * @param id the id of the element to find 
+	 * @param collection the collection to search for the element 
+	 * @return the element with the specified id, or {@code null} if such element doesn't exist 
+	 */
 	public static DiagramElement findElement(Long id, Collection<DiagramElement> collection){
 		for(DiagramElement e : collection)
 			if(e.getId() == id)
@@ -84,6 +143,16 @@
 		return null;
 	}
 	
+	/**
+	 * Finds a element (node or edge) with an identity hash code in a {@code Collection} of diagram elements. If none of the elements
+	 * has the code passed as argument, {@code null} is returned.  
+	 * 
+	 * @param identityHashcode the identity hash code of the element to find 
+	 * @param collection the collection to search for the element 
+	 * @return the element with the specified identity hash code, or {@code null} if such element doesn't exist
+	 * 
+	 *  @see Object#hashCode()
+	 */
 	public static DiagramElement findElementByHashcode(long identityHashcode, Collection<DiagramElement> collection){
 		for(DiagramElement de : collection){
 			if(System.identityHashCode(de) == identityHashcode){
@@ -94,12 +163,13 @@
 	}
 	
 	/**
-	 * Return the tree node whose path is described by the variable path
-	 * where path contains the indexes returned by each node n of the path upon calling n.getParent().getChildAt(n) 
+	 * Returns the tree node whose path is described by the variable path
+	 * where path contains the indexes returned by each node n of the 
+	 * path upon calling n.getParent().getChildAt(n). 
 	 * 
-	 * @param path
-	 * @param root
-	 * @return
+	 * @param path the path for the node in the tree 
+	 * @param root the node where the path starts
+	 * @return the node at the specified path, or {@code null} otherwise
 	 */
 	public static DiagramTreeNode findTreeNode(int[] path, DiagramTreeNode root){
 		DiagramTreeNode retVal = root;
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphElement.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphElement.java	Tue Jul 10 22:39:37 2012 +0100
@@ -61,7 +61,7 @@
 	 * This method is to be called when the motion (e.g. a translation) is over. 
 	 * Note that for instance a translation might be composed on several calls to {@code translate}
 	 * 
-	 * @param source
+	 * @param source the source of the motion action
 	 */
 	public void stopMove(Object source);
 	
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphPanel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -61,7 +61,7 @@
 	/**
 	 * Constructs a graph.
 	 * @param aDiagram a diagram to paint in the graph 
-	 * @param a aToolbar a toolbar containing the node and edges prototypes for creating 
+	 * @param aToolbar a toolbar containing the node and edges prototypes for creating 
 	 * elements in the graph. 
 	 */
 
@@ -85,7 +85,7 @@
 		selectedElements = new HashSet<DiagramElement>();
 		moveLockedElements = new HashSet<Object>();
 
-		toolbar.addEdgeCreatedListener(new innerEdgeListener());
+		toolbar.setEdgeCreatedListener(new innerEdgeListener());
 
 		/* 	---- COLLECTION LISTENER ----
 		 * Adding a collection listener. This listener reacts at changes in the model 
@@ -156,7 +156,11 @@
 				Object tool = toolbar.getSelectedTool();
 				/* - right click - */
 				if((event.getModifiers() & InputEvent.BUTTON1_MASK) == 0) {
-					if(e != null){
+					if(n != null){
+						CCmIPopupMenu.NodePopupMenu pop = new CCmIPopupMenu.NodePopupMenu(n,GraphPanel.this,modelUpdater,selectedElements);
+						nodePopup = pop;
+						pop.show(GraphPanel.this, event.getX(), event.getY());
+					}else if(e != null){
 						if( e.contains(mousePoint)){
 							Node extremityNode = e.getClosestNode(mousePoint,EDGE_END_MIN_CLICK_DIST);
 							if(extremityNode == null){ // click far from the attached nodes, only prompt with set name item
@@ -169,12 +173,9 @@
 								pop.show(GraphPanel.this, event.getX(), event.getY());
 							}
 						}
-					}else if(n != null){
-						CCmIPopupMenu.NodePopupMenu pop = new CCmIPopupMenu.NodePopupMenu(n,GraphPanel.this,modelUpdater,selectedElements);
-						nodePopup = pop;
-						pop.show(GraphPanel.this, event.getX(), event.getY());
-					}else
+					}else {
 						return;
+					}
 				}
 
 				/* - one click && palette == select - */
@@ -368,6 +369,13 @@
 		paintGraph(g);
 	}
 
+	/**
+	 * Paints the graph on the graphics passed as argument. This function is called 
+	 * each time the component is painted. 
+	 * 
+	 * @see #paintComponent(Graphics)
+	 * @param g the graphics object used to paint this graph
+	 */
 	public void paintGraph(Graphics g){
 		Graphics2D g2 = (Graphics2D) g;
 		g2.translate(-minX, -minY);
@@ -541,12 +549,23 @@
 	}
 
 	/**
-   Gets the smallest rectangle enclosing the graph
-   @return the bounding rectangle
+   	 *	Gets the smallest rectangle enclosing the graph
+   	 *	@return the bounding rectangle
 	 */
 	public Rectangle2D getMinBounds() { return minBounds; }
+	
+	/**
+	 * Sets the smallest rectangle enclosing the graph
+	 * @param newValue the bounding rectangle
+	 */
 	public void setMinBounds(Rectangle2D newValue) { minBounds = newValue; }
 
+	/**
+	 * Returns the smallest rectangle enclosing the graph, that is all the nodes 
+	 * and all the edges in the diagram.
+	 * 
+	 * @return the bounding rectangle
+	 */
 	public Rectangle2D getGraphBounds(){
 		Rectangle2D r = minBounds;
 		for (Node n : nodes){
@@ -561,6 +580,12 @@
 				r.getWidth() + Node.SHADOW_GAP + Math.abs(minX), r.getHeight() + Node.SHADOW_GAP + Math.abs(minY));
 	}
 
+	/**
+	 * Sets the model updater for this graph. The model updater is used 
+	 * to make changes to the diagram (e.g. adding, removing, renaming nodes and edges)
+	 *  
+	 * @param modelUpdater the model updater for this graph panel
+	 */
 	public void setModelUpdater(DiagramModelUpdater modelUpdater){
 		this.modelUpdater = modelUpdater;
 	}
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphToolbar.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/GraphToolbar.java	Tue Jul 10 22:39:37 2012 +0100
@@ -46,8 +46,10 @@
 */
 @SuppressWarnings("serial")
 public class GraphToolbar extends JToolBar {
-   /**
-      Constructs a tool bar with no icons.
+  /**
+   * Constructs a tool bar with no icons.
+   * 
+   * @param diagram the diagram this toolbar is related to 
    */
    public GraphToolbar(Diagram diagram){
       /* creates icon for select button */
@@ -97,7 +99,7 @@
    /**
       Gets the node prototype that is associated with
       the currently selected button
-      @return a Node or Edge prototype
+      @return a {@code Node} prototype
    */
    public Node getSelectedTool() {
 	   @SuppressWarnings("rawtypes")
@@ -117,9 +119,9 @@
    /**
       Adds a node to the tool bar.
       @param n the node to add
-      @param tip the tool tip
+      @param tip the tool tip appearing when hovering on this edge button 
    */
-   public void add(final Node n, String text){
+   public void add(final Node n, String tip){
       Icon icon = new Icon(){
             public int getIconHeight() { return BUTTON_SIZE; }
             public int getIconWidth() { return BUTTON_SIZE; }
@@ -143,7 +145,7 @@
          };
 
       NodeButton button = new NodeButton(n, icon);
-      button.setToolTipText(text);
+      button.setToolTipText(tip);
       
       add(button);
       nodeButtonsGroup.add(button);
@@ -151,10 +153,10 @@
    
    /**
       Adds an edge to the tool bar.
-      @param n the node to add
-      @param tip the tool tip
+      @param e the edge to add
+      @param tip the tool tip appearing when hovering on this edge button 
    */
-   public void add(final Edge e, String text){
+   public void add(final Edge e, String tip){
        Icon icon = new Icon(){
             public int getIconHeight() { return BUTTON_SIZE; }
             public int getIconWidth() { return BUTTON_SIZE; }
@@ -189,7 +191,7 @@
             }
          };
       final JButton button = new JButton(icon);               
-      button.setToolTipText(text);
+      button.setToolTipText(tip);
       button.setFocusable(false);
       
       button.addActionListener(new ActionListener(){
@@ -200,7 +202,13 @@
       add(button);
    }
    
-   public void addEdgeCreatedListener(EdgeCreatedListener edgeCreatedListener){
+   /**
+    * Sets the {@code EdgeCreatedListener} for this toolbar. Any previous set listener
+    * will be overwritten. 
+    * 
+    * @param edgeCreatedListener the new {@code EdgeCreatedListener} for this toolbar  
+    */
+   public void setEdgeCreatedListener(EdgeCreatedListener edgeCreatedListener){
 	   this.edgeCreatedListener = edgeCreatedListener;
    }
    
@@ -217,7 +225,17 @@
 	   Node node;
    }
    
+   /**
+    * The listener interface receiving events when the user clicks on 
+    * an {@code Edge} button. Unlike {@code Node} buttons which are {@code JTobbleButton}
+    * objects to select the {@code Node} returned by {@code getSelectedTool()}, 
+    * the {@code Edge} buttons just trigger the registered listener with an immediate effect.
+    */
    public interface EdgeCreatedListener {
+	   /**
+	    * Invoked when an {@code Edge} button is pressed.
+	    * @param e the {@code Edge} related to the pressed button
+	    */
 		void edgeCreated(Edge e);
    }
    
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/HapticKindle.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/HapticKindle.java	Tue Jul 10 22:39:37 2012 +0100
@@ -27,6 +27,7 @@
 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel;
 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
 import uk.ac.qmul.eecs.ccmi.haptics.HapticListener;
+import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerThread;
 import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand;
 import uk.ac.qmul.eecs.ccmi.main.DiagramEditorApp;
 import uk.ac.qmul.eecs.ccmi.network.Command;
@@ -39,20 +40,23 @@
 /**
  * 
  * An instance of HapticListener for the diagram editor. By this class visual diagrams 
- * can be manipulated by an haptic device.  
+ * can be manipulated by an haptic device. This class extends the {@code Thread} class,
+ * and can therefore be run on a separate thread listening to the haptic commands coming from the thread
+ * managing the haptic device. The commands affecting the Swing components will
+ * be queued for execution on the code Event Dispatching Thread event queue. 
  *
  */
-public class HapticKindle extends HapticListener {
+public class HapticKindle extends HapticListenerThread {
 	
 	public HapticKindle(){
 		super();
+		cmdImpl = new CommandImplementation();
+		/* unselect always ends up to the same instruction. Therefore don't create a new runnable * 
+		 * each time the command is issued, but rather keep and reuse always the same class       */
 		unselectRunnable = new Runnable(){
 			@Override
 			public void run(){
-				EditorFrame frame = DiagramEditorApp.getFrame();
-				if((frame == null)||(frame.getActiveTab() == null))
-					return;
-				frame.selectHapticHighligh(null);
+				cmdImpl.executeCommand(HapticListenerCommand.UNSELECT, 0, 0, 0, 0, 0);
 			}
 		};
 	}
@@ -62,57 +66,26 @@
 	 * diagram model, are executed in the Event Dispatching Thread through {@code SwingUtilities} invoke
 	 * methods. This prevents race conditions on the model and on diagram elements. 
 	 * 
-	 * @see HapticListener#executeCommand(HapticListenerCommand, int, double, double, double, double)
+	 * @see HapticListenerThread#executeCommand(HapticListenerCommand, int, double, double, double, double)
 	 */
 	@Override
-	public void executeCommand(HapticListenerCommand cmd, final int ID, final double x, final double y, final double startX, final double startY) {
-		final EditorFrame frame = DiagramEditorApp.getFrame();
+	public void executeCommand(final HapticListenerCommand cmd, final int ID, final double x, final double y, final double startX, final double startY) {
 		switch(cmd){
 		case PLAY_ELEMENT_SOUND :  
+		case PLAY_ELEMENT_SPEECH : 
+		case SELECT :
+		case INFO :
+		case ERROR :
 			SwingUtilities.invokeLater(new Runnable(){
 				public void run(){
-					if((frame == null)||(frame.getActiveTab() == null))
-						return;
-					CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-					DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-					/* can be null if the tab has been switched or closed in the meantime */
-					if(de == null)
-						return;
-					SoundFactory.getInstance().play(de.getSound());
-				}
-			});
-			break;
-		case PLAY_ELEMENT_SPEECH : 
-			SwingUtilities.invokeLater(new Runnable(){
-				public void run(){
-					if((frame == null)||(frame.getActiveTab() == null))
-						return;
-					CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-					DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-					if(de == null)
-						return;
-					SoundFactory.getInstance().play(de.getSound());
-					NarratorFactory.getInstance().speak(de.getName());
-					iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName());
-				}
-			});
-			break;
-		case SELECT :
-			SwingUtilities.invokeLater(new Runnable(){
-				public void run(){
-					if((frame == null)||(frame.getActiveTab() == null))
-						return;
-					CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-					DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-					if(de == null)
-						return;
-					frame.selectHapticHighligh(de);
+					cmdImpl.executeCommand(cmd, ID, x, y, startX, startY);
 				}
 			});
 			break;
 		case UNSELECT :
 			SwingUtilities.invokeLater(unselectRunnable);
 			break;
+		case PICK_UP :
 		case MOVE : {
 			/* when this block is executed we already have the lock * 
 			 * on the element from the PICK_UP command execution    */
@@ -120,144 +93,194 @@
 				SwingUtilities.invokeAndWait(new Runnable(){
 					@Override
 					public void run(){
-						if((frame == null)||(frame.getActiveTab() == null))
-							return;
-						CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-						DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-						if(de == null)
-							return;
-						DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
-						if(de instanceof Node){
-							Node n = (Node)de;
-							Rectangle2D bounds = n.getBounds();
-							Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
-							double dx = x - p.getX();
-							double dy = y - p.getY();
-							n.getMonitor().lock();
-							modelUpdater.translate(n, p, dx, dy,DiagramEventSource.HAPT);
-							modelUpdater.stopMove(n,DiagramEventSource.HAPT);
-							n.getMonitor().unlock();
-
-							StringBuilder builder = new StringBuilder();
-							builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX())
-							.append(' ').append(p.getY());
-							iLog("move node start",builder.toString());
-							builder = new StringBuilder();
-							builder.append(DiagramElement.toLogString(n)).append(' ')
-							.append(x).append(' ').append(y);
-							iLog("move node end",builder.toString());
-						}else{
-							Edge e = (Edge)de;
-							modelUpdater.startMove(e, new Point2D.Double(startX,startY),DiagramEventSource.HAPT);
-							Point2D p  = new Point2D.Double(x,y);
-							e.getMonitor().lock();
-							modelUpdater.bend(e, p,DiagramEventSource.HAPT);
-							modelUpdater.stopMove(e,DiagramEventSource.HAPT);
-							e.getMonitor().unlock();
-							
-							StringBuilder builder = new StringBuilder();
-							builder.append(DiagramElement.toLogString(e)).append(' ').append(startX)
-							.append(' ').append(startY);
-							iLog("bend edge start",builder.toString());
-							builder = new StringBuilder();
-							builder.append(DiagramElement.toLogString(e)).append(' ')
-							.append(x).append(' ').append(y);
-							iLog("bend edge end",builder.toString());
-						}
-						modelUpdater.yieldLock(de,
-								Lock.MOVE,
-								new DiagramEventActionSource(
-										DiagramEventSource.HAPT,
-										de instanceof Node ? Command.Name.STOP_NODE_MOVE : Command.Name.STOP_EDGE_MOVE,
-										de.getId(),
-										de.getName()
-										));
+						cmdImpl.executeCommand(cmd, ID, x, y, startX, startY);
 					} // run()
 				});
 			} catch (Exception e) {
 				throw new RuntimeException(e);
 			}
-			SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
 		}
 			break;
-		case INFO : 
-			SwingUtilities.invokeLater(new Runnable(){
-				public void run(){
-					if((frame == null)||(frame.getActiveTab() == null))
-						return;
-					CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-					DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-					if(de == null)
-						return;
-					SoundFactory.getInstance().stop();
-					NarratorFactory.getInstance().speak(de.detailedSpokenText());
-					iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName());					
+		 
+		case PLAY_SOUND :
+			cmdImpl.executeCommand(cmd, ID, x, y, startX, startY); // not in the Event Dispatching Thread
+			break;
+		}
+		
+	}
+
+	/**
+	 * Returns the delegate inner implementation of the {@code HapticListener} commands.
+	 * When called directly from the returned {@code HapticListener} the commands won't be executed on a separate thread. 
+	 * 
+	 * @return the {@code HapticListener} command implementation
+	 */
+	@Override
+	public HapticListener getNonRunnableListener(){
+		return cmdImpl;
+	}
+		
+	
+	private Runnable unselectRunnable;
+	private CommandImplementation cmdImpl;
+	private static String INTERACTION_LOG_SOURCE = "HAPTIC"; 
+
+	/* An inner class with the implementation of all the commands. HapticKindle runs  *
+	 * on its own thread and delegates the real commands implementation to this class */
+	private static class CommandImplementation implements HapticListener{
+		@Override
+		public void executeCommand(HapticListenerCommand cmd, int ID, double x,
+				double y, double startX, double startY) {
+			final EditorFrame frame = DiagramEditorApp.getFrame();
+			switch(cmd){
+			case PLAY_ELEMENT_SOUND : {
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				/* can be null if the tab has been switched or closed in the meantime */
+				if(de == null)
+					return;
+				SoundFactory.getInstance().play(de.getSound());
+			}break;
+			case PLAY_ELEMENT_SPEECH : { 
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				if(de == null)
+					return;
+				SoundFactory.getInstance().play(de.getSound());
+				NarratorFactory.getInstance().speak(de.getName());
+				iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName());
+			}break;
+			case SELECT : {
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				if(de == null)
+					return;
+				frame.selectHapticHighligh(de);
+			}break;
+			case UNSELECT : {
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				frame.selectHapticHighligh(null);
+			}break;
+			case MOVE : {
+				/* when this block is executed we already have the lock * 
+				 * on the element from the PICK_UP command execution    */
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				if(de == null)
+					return;
+				DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
+				if(de instanceof Node){
+					Node n = (Node)de;
+					Rectangle2D bounds = n.getBounds();
+					Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
+					double dx = x - p.getX();
+					double dy = y - p.getY();
+					n.getMonitor().lock();
+					modelUpdater.translate(n, p, dx, dy,DiagramEventSource.HAPT);
+					modelUpdater.stopMove(n,DiagramEventSource.HAPT);
+					n.getMonitor().unlock();
+
+					StringBuilder builder = new StringBuilder();
+					builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX())
+					.append(' ').append(p.getY());
+					iLog("move node start",builder.toString());
+					builder = new StringBuilder();
+					builder.append(DiagramElement.toLogString(n)).append(' ')
+					.append(x).append(' ').append(y);
+					iLog("move node end",builder.toString());
+				}else{
+					Edge e = (Edge)de;
+					modelUpdater.startMove(e, new Point2D.Double(startX,startY),DiagramEventSource.HAPT);
+					Point2D p  = new Point2D.Double(x,y);
+					e.getMonitor().lock();
+					modelUpdater.bend(e, p,DiagramEventSource.HAPT);
+					modelUpdater.stopMove(e,DiagramEventSource.HAPT);
+					e.getMonitor().unlock();
+					
+					StringBuilder builder = new StringBuilder();
+					builder.append(DiagramElement.toLogString(e)).append(' ').append(startX)
+					.append(' ').append(startY);
+					iLog("bend edge start",builder.toString());
+					builder = new StringBuilder();
+					builder.append(DiagramElement.toLogString(e)).append(' ')
+					.append(x).append(' ').append(y);
+					iLog("bend edge end",builder.toString());
 				}
-			});
-			break;
-		case PLAY_SOUND :
-			switch(HapticListenerCommand.Sound.fromInt(ID) ){
-			case MAGNET_OFF : 
-				SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF);
-				iLog("sticky mode off","");
-				break;
-			case MAGNET_ON : 
-				SoundFactory.getInstance().play(SoundEvent.MAGNET_ON);
-				iLog("sticky mode on","");
-				break;
-			case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
-				break;
-			}
-			break;
-		case PICK_UP :
-			try {
-				SwingUtilities.invokeAndWait(new Runnable (){
-					@Override
-					public void run(){
+				modelUpdater.yieldLock(de,
+						Lock.MOVE,
+						new DiagramEventActionSource(
+								DiagramEventSource.HAPT,
+								de instanceof Node ? Command.Name.STOP_NODE_MOVE : Command.Name.STOP_EDGE_MOVE,
+								de.getId(),
+								de.getName()
+								));
+				SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
+			}break;
+			case INFO : {
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				if(de == null)
+					return;
+				SoundFactory.getInstance().stop();
+				NarratorFactory.getInstance().speak(de.detailedSpokenText());
+				iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName());					
+			}break;
+			case PLAY_SOUND : {
+				switch(HapticListenerCommand.Sound.fromInt(ID) ){
+				case MAGNET_OFF : 
+					SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF);
+					iLog("sticky mode off","");
+					break;
+				case MAGNET_ON : 
+					SoundFactory.getInstance().play(SoundEvent.MAGNET_ON);
+					iLog("sticky mode on","");
+					break;
+				case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
+					break;
+				}
+			}break;
+			case PICK_UP :{
+				if((frame == null)||(frame.getActiveTab() == null))
+					return;
+				CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
+				DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
+				if(de == null)
+					return;
+				DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
+				if(!modelUpdater.getLock(de, 
+						Lock.MOVE,
+						new DiagramEventActionSource(DiagramEventSource.HAPT, de instanceof Edge ? Command.Name.TRANSLATE_EDGE : Command.Name.TRANSLATE_NODE ,de.getId(),de.getName()))){
+					iLog("Could not get lock on element for motion", DiagramElement.toLogString(de));
+					NarratorFactory.getInstance().speak("Object is being moved by another user");
+					return;
+				}
+				frame.hPickUp(de);
+				SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
+				iLog("hook on","");
+			}break;
+			case ERROR : {
 						if((frame == null)||(frame.getActiveTab() == null))
 							return;
-						CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
-						DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
-						if(de == null)
-							return;
-						DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
-						if(!modelUpdater.getLock(de, 
-								Lock.MOVE,
-								new DiagramEventActionSource(DiagramEventSource.HAPT, de instanceof Edge ? Command.Name.TRANSLATE_EDGE : Command.Name.TRANSLATE_NODE ,de.getId(),de.getName()))){
-							iLog("Could not get lock on element for motion", DiagramElement.toLogString(de));
-							NarratorFactory.getInstance().speak("Object is being moved by another user");
-							return;
-						}
-						frame.hPickUp(de);
-						SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
-						iLog("hook on","");
-					}
-				});
-			}catch(Exception e){
-				e.printStackTrace();
-				throw new RuntimeException();	
+						frame.backupOpenDiagrams();
+				NarratorFactory.getInstance().speak(ResourceBundle.getBundle(EditorFrame.class.getName()).getString("speech.haptic_device_crashed"));
+			}break;
 			}
-			break;
-		case ERROR : 
-			/* no synchronization necessary as the XMLManager looks after it*/
-			SwingUtilities.invokeLater(new Runnable(){
-				@Override
-				public void run(){
-					if((frame == null)||(frame.getActiveTab() == null))
-						return;
-					frame.backupOpenDiagrams();
-				}
-			});
-			NarratorFactory.getInstance().speak(ResourceBundle.getBundle(EditorFrame.class.getName()).getString("speech.haptic_device_crashed"));
-			break;
+		}
+		
+		private void iLog(String action, String args){
+			InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
 		}
 	}
 	
-	private void iLog(String action, String args){
-		InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
-	}
-	
-	private Runnable unselectRunnable;
-	private static String INTERACTION_LOG_SOURCE = "HAPTIC"; 
-	//private EditorFrame frame;
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/LineStyle.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/LineStyle.java	Tue Jul 10 22:39:37 2012 +0100
@@ -60,8 +60,8 @@
 	 * Returns an a bit representation of the stippling of this edge. 
 	 * This value can be used by openGL like libraries to draw the edge and it's used by 
 	 * the OmniHaptic device native code to paint the edge visually and haptically.  
-	 * See also {@link http://www.opengl.org/sdk/docs/man/xhtml/glLineStipple.xml}
-	 *
+	 * 
+	 * @see <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLineStipple.xml">glLineStipple</a>
 	 * 
 	 * @return an int with the bit representation of the stipple pattern
 	 */
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Lock.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Lock.java	Tue Jul 10 22:39:37 2012 +0100
@@ -24,13 +24,44 @@
  *
  */
 public enum Lock {
+	/**
+	 * The element will be deleted, therefore no other operation on the same element is allowed to any other user.
+	 */
 	DELETE,
+	/**
+	 * The element will be renamed, therefore no renaming of the same element is allowed to any other user.
+	 */
 	NAME,
+	/**
+	 * The node properties will be edit, therefore no properties or modifiers editing 
+	 * on the same node will be allowed to any other user.
+	 */
 	PROPERTIES,
+	/**
+	 * The edge end is being edited (label or arrow head), therefore no end editing 
+	 * on the same edge will be allowed to any other user.
+	 */
 	EDGE_END,
+	/**
+	 * The element is being moved, therefore no move on the same element will be allowed to any other user.
+	 */
 	MOVE,
+	/**
+	 * The notes of the tree node will be edited, therefore no notes editing 
+	 * on the same tree node will be allowed to any other user.
+	 */
 	NOTES,
+	/**
+	 * The bookmarks of the tree node will be edited, therefore no bookmarks editing 
+	 * on the same tree node will be allowed to any other user.
+	 */
 	BOOKMARK,
+	/**
+	 * The element cannot be deleted by other users. 
+	 */
 	MUST_EXIST,
+	/**
+	 * {@code null} value.
+	 */
 	NONE
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/LoopComboBox.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/LoopComboBox.java	Tue Jul 10 22:39:37 2012 +0100
@@ -73,7 +73,7 @@
 		}
 	}
 	
-	/**
+	/*
 	 * when the comboBox has only one item the ItemStateChanged listeners ain't fired by default.
 	 * This behaviour has to be forced in order to have the item label to be spoken out by  
 	 * the narrator, in spite of the item number .  
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/ModifierEditorDialog.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/ModifierEditorDialog.java	Tue Jul 10 22:39:37 2012 +0100
@@ -35,10 +35,8 @@
 import uk.ac.qmul.eecs.ccmi.utils.GridBagUtilities;
 
 /**
- * 
  * A dialog showing a list of checkboxes. By selecting the checkboxes the user can choose
  * which modifiers are assigned to a property value. 
- *
  */
 @SuppressWarnings("serial")
 public class ModifierEditorDialog extends JDialog {
@@ -82,6 +80,16 @@
 		pack();
 	}
 	
+	/**
+	 * Shows a dialog with the checkboxes for the user to tick. 
+	 * 
+	 * @param parent the parent JDialog this dialog will appear in front of
+	 * @param modifierTypes a list of the modifier that will be shown to the user, each near a checkbox
+	 * @param modifiers a set of modifiers indexes. The {@code modifierTypes} at the specified indexes will 
+	 * be shown as already ticked  
+	 * 
+	 * @return a reference to {@code modifiers} after it has been updated according to the user selections. 
+	 */
 	public static Set<Integer> showDialog(JDialog parent, List<String> modifierTypes, Set<Integer> modifiers){
 		ModifierEditorDialog.modifiers = modifiers;
 		dialog = new ModifierEditorDialog(parent, modifierTypes, modifiers);
@@ -91,6 +99,16 @@
 		
 	}
 	
+	/**
+	 * Shows a dialog with the checkboxes for the user to tick. 
+	 * 
+	 * @param parent the parent Frame this dialog will appear in front of
+	 * @param modifierTypes a list of the modifier that will be shown to the user, each near a checkbox
+	 * @param modifiers a set of modifiers indexes. The {@code modifierTypes} at the specified indexes will 
+	 * be shown as already ticked  
+	 * 
+	 * @return a reference to {@code modifiers} after it has been updated according to the user selections. 
+	 */
 	public static Set<Integer> showDialog(Frame parent, List<String> modifierTypes, Set<Integer> modifiers){
 		ModifierEditorDialog.modifiers = modifiers;
 		dialog = new ModifierEditorDialog(parent, modifierTypes, modifiers);
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/Node.java	Tue Jul 10 22:39:37 2012 +0100
@@ -35,7 +35,6 @@
 import org.w3c.dom.NodeList;
 
 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramEdge;
-import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode;
 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramNode;
 import uk.ac.qmul.eecs.ccmi.diagrammodel.ElementChangedEvent;
 import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties;
@@ -43,19 +42,24 @@
 import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;
 
 /**
- * An node in a graph. Node objects are used in a GraphPanel to render diagram nodes visually. 
- * Node objects are used in the tree representation of the diagram as well, as they're
+ * 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);
-		internalNodes = new ArrayList<Node>();
 		attachedEdges = new ArrayList<Edge>();
-		externalNode = null;
 	}
 
 	/* --- DiagramNode abstract methods implementation --- */
@@ -81,35 +85,32 @@
 
 	@Override
 	public Node getExternalNode(){
-		return externalNode; 
+		return null; 
 	}
 
 	@Override
 	public void setExternalNode(DiagramNode node){
-		externalNode = (Node)node;
+		throw new UnsupportedOperationException(); 
 	}
 
 	@Override
 	public  Node getInternalNodeAt(int i){
-		return internalNodes.get(i); 
+		throw new UnsupportedOperationException(); 
 	}
 	
 	@Override
 	public int getInternalNodesNum(){
-		return internalNodes.size();
+		return 0;
 	}
 
 	@Override
 	public void addInternalNode(DiagramNode node){
-		internalNodes.add((Node)node);
+		throw new UnsupportedOperationException(); 
 	}
 
 	@Override
 	public void removeInternalNode(DiagramNode node){
-		if (node.getExternalNode() != this) 
-			return;
-		internalNodes.remove(node);
-		node.setExternalNode(null);
+		throw new UnsupportedOperationException(); 
 	}
 
 	@Override
@@ -122,12 +123,7 @@
 		}
 	}
 
-	/**
-	 * 	Translates the node by a given amount
-	 *	@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
-	 */
+	@Override
 	public void translate( Point2D p , double dx, double dy, Object source){
 		translateImplementation( p, dx, dy);
 		for(int i=0; i< getInternalNodesNum();i++){
@@ -136,9 +132,6 @@
 		notifyChange(new ElementChangedEvent(this, this, "translate", source));
 	}
 
-	/**
-	 * @see DiagramTreeNode#setNotes(String)
-	 */
 	@Override
 	protected void setNotes(String notes,Object source){
 		this.notes = notes;
@@ -163,10 +156,6 @@
 	 */
 	public abstract boolean contains(Point2D aPoint);
 
-	/**
-	 * Get the bounding rectangle of the shape of this node
-	 * @return the bounding rectangle
-	 */
 	@Override
 	public abstract Rectangle2D getBounds();
 
@@ -175,6 +164,7 @@
 		/* useless, here just to comply with the GraphElement interface */
 	}
 
+	@Override
 	public abstract Point2D getConnectionPoint(Direction d);
 
 	@Override
@@ -188,8 +178,21 @@
 		}
 	}
 
+	/**
+	 * 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());
 
@@ -240,6 +243,15 @@
 		}
 	}
 
+	/**
+	 * 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
+	 * 
+	 * @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{
@@ -339,17 +351,19 @@
 	@Override
 	public Object clone(){
 		Node clone = (Node)super.clone();
-		clone.internalNodes = (ArrayList<Node>) internalNodes.clone();
-		clone.externalNode = null;
 		clone.attachedEdges = (ArrayList<Edge>) attachedEdges.clone(); 
 		return clone;
 	}
 
-	protected ArrayList<Node> internalNodes;
-	protected Node externalNode;
+	/**
+	 * 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;
 
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyEditorDialog.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyEditorDialog.java	Tue Jul 10 22:39:37 2012 +0100
@@ -89,6 +89,17 @@
 		pack();
 	}
 	
+	/**
+	 * A static method to show a {@code PropertyEditorDialog}. Via this dialog a user can 
+	 * specify the entries of a {@code NodeProperties} object and their modifiers, if any have been 
+	 * defined during the template creation.  
+	 * 
+	 * @param parent The parent {@code Frame} of the dialog 
+	 * @param properties an instance of {@code NodeProeprties} whose value will be shown in the dialog 
+	 * for the user to edit
+	 * @return a reference to {@code properties} containing the new values entered by the user or 
+	 * {@code null} if the user presses the cancel button or closes the window. 
+	 */
 	public static NodeProperties showDialog(Frame parent, NodeProperties properties){
 		if(properties == null)
 			throw new IllegalArgumentException(resources.getString("dialog.property_editor.error.property_null"));		
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyTableModel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/PropertyTableModel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -29,13 +29,20 @@
 
 
 /**
- * 
- * A table model containing the property values currently edited and the modifiers assigned to them
+ * A table model containing the property values currently edited in a table of a {@code PropertyEditorDialog} 
+ * and the modifiers assigned to them
  * in the form of an array of indexes pointing to the modifiers types  
  */
 @SuppressWarnings("serial")
 public class PropertyTableModel extends AbstractTableModel {
-
+	
+	/**
+	 * Construct a new {@code PropertyTableModel}.
+	 * 
+	 * @param propertyType the type of the node property related to this table model 
+	 * @param values the list of values for this node property
+	 * @param modifiers the modifiers for this node property 
+	 */
 	public PropertyTableModel(String propertyType, List<String> values, Modifiers modifiers ){
 		data = new ArrayList<ModifierString>();
 		for(int i = 0; i< values.size();i++){
@@ -88,23 +95,44 @@
 		return true;
 	}
 	
+	/**
+	 * Returns the indexes (pointing to the modifiers types) of the property value at the specified row in the table
+	 * with this model.
+	 * 
+	 * @param row the row of the property value 
+	 * @return the modifiers type indexes for the specified property value 
+	 */
 	public Set<Integer> getIndexesAt(int row){
 		return data.get(row).modifierIndexes;
 	}
 	
+	/**
+	 * Set the the indexes (pointing to the modifiers types) of the property value at the specified row in the table
+	 * with this model.
+	 * 
+	 * @param row he row of the property value 
+	 * @param indexes  the modifiers type indexes for the specified property value 
+	 */
 	public void setIndexesAt(int row, Set<Integer> indexes){
 		data.get(row).modifierIndexes = new HashSet<Integer>();
 		data.get(row).modifierIndexes.addAll(indexes);
 	}
 	
+	/**
+	 * Set the the indexes (pointing to the modifiers types) of the property value at the specified row in the table
+	 * with this model.
+	 * 
+	 * @param row he row of the property value 
+	 * @param indexes  the modifiers type indexes for the specified property value 
+	 */
 	public void setIndexesAt(int row, Integer[] indexes){
 		data.get(row).modifierIndexes = new HashSet<Integer>();
 		for(int i=0; i<indexes.length; i++)
 			data.get(row).modifierIndexes.add(indexes[i]);
 	}
 	
-	List<ModifierString> data;
-	String columnName;
+	private List<ModifierString> data;
+	private String columnName;
 	
 	private class ModifierString  {
 		ModifierString(String value, Set<Integer> s){
@@ -134,8 +162,8 @@
 			return value;
 		}
 		
-		String value;
-		Set<Integer> modifierIndexes;
+		private String value;
+		private Set<Integer> modifierIndexes;
 	}
 
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/ResourceFactory.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/ResourceFactory.java	Tue Jul 10 22:39:37 2012 +0100
@@ -20,30 +20,26 @@
 
 package uk.ac.qmul.eecs.ccmi.gui;
 
-import java.awt.Image;
 import java.awt.event.ActionListener;
 import java.beans.EventHandler;
-import java.net.URL;
 import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
-import javax.swing.Action;
-import javax.swing.ImageIcon;
-import javax.swing.JButton;
 import javax.swing.JMenu;
 import javax.swing.JMenuBar;
 import javax.swing.JMenuItem;
 import javax.swing.KeyStroke;
 
 /**
- * A factory class for swing components creation support  
- *
+ * A factory class for swing components creation support. Components that are created via this 
+ * class are sonyfied via the Narrator
  */
-public class ResourceFactory{
+class ResourceFactory{
    public ResourceFactory(ResourceBundle bundle){
       this.bundle = bundle;
    }
 
+   
    public JMenuBar createMenuBar(){
 	   return SpeechMenuFactory.getMenuBar();
    }
@@ -121,80 +117,6 @@
       }
       return menu;
    }
-      
-   public JButton createButton(String prefix) {
-      String text = bundle.getString(prefix + ".text");
-      JButton button = new JButton(text);
-      try {
-         String mnemonic = bundle.getString(prefix + ".mnemonic");
-         button.setMnemonic(mnemonic.charAt(0));
-      }
-      catch (MissingResourceException exception) {
-         // ok not to set mnemonic
-      }
-
-      try {
-         String tooltip = bundle.getString(prefix + ".tooltip");
-         button.setToolTipText(tooltip);         
-      }
-      catch (MissingResourceException exception) {
-         // ok not to set tooltip
-      }
-      return button;
-   }
    
-   
-   public Action configureAction(String prefix, Action action)
-   {
-      try
-      {
-         String text = bundle.getString(prefix + ".text");
-         action.putValue(Action.NAME, text);
-      }
-      catch (MissingResourceException exception)
-      {
-         // ok not to set name
-      }
-
-      try
-      {
-         String mnemonic = bundle.getString(prefix + ".mnemonic");
-         action.putValue(Action.MNEMONIC_KEY, new Integer(mnemonic.charAt(0)));
-      }
-      catch (MissingResourceException exception)
-      {
-         // ok not to set mnemonic
-      }
-
-      try {
-         String accelerator = bundle.getString(prefix + ".accelerator");
-         action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(accelerator));
-      }
-      catch (MissingResourceException exception){
-         // ok not to set accelerator
-      }
-
-      try
-      {
-         String tooltip = bundle.getString(prefix + ".tooltip");
-         action.putValue(Action.SHORT_DESCRIPTION, tooltip);         
-      }
-      catch (MissingResourceException exception)
-      {
-         // ok not to set tooltip
-      }
-      return action;
-   }
-   
-   public static class ImageFactory{ 
-	   public Image getImage(String path){
-		   URL url = getClass().getResource(path);
-		   if(url != null)
-				return new ImageIcon(url).getImage();
-		   else
-			   return null;
-	   }
-   }
-
    private ResourceBundle bundle;
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechMenuFactory.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechMenuFactory.java	Tue Jul 10 22:39:37 2012 +0100
@@ -41,15 +41,14 @@
 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
 
-/**
- * 
+/*
  * This class provides a version of JMenuItem, JCheckBox, and JMenu which emit an "error" sound when 
  * they're disabled and the user tries to use it by an accelerator
  */
 @SuppressWarnings("serial")
-public class SpeechMenuFactory extends JMenu {
+class SpeechMenuFactory extends JMenu {
 
-	/* implements the singleton pattern and keeps a static reference to the menuBar used bu JMenuItems and JMenus */
+	/* implements the singleton pattern and keeps a static reference to the menuBar used by JMenuItems and JMenus */
 	public static JMenuBar getMenuBar(){
 		if(menuBar == null){
 			menuBar = new JMenuBar(){
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechOptionPane.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechOptionPane.java	Tue Jul 10 22:39:37 2012 +0100
@@ -67,8 +67,6 @@
  * cancelling the option.   
  * Furthermore, this class provides one-line calls to display accessible dialog boxes. Input by the user as well 
  * as focused components are spoken out through text to speech synthesis performed by a {@link Narrator} instance.
- *   
- *  
  */
 public class SpeechOptionPane {
 	
@@ -96,7 +94,7 @@
 	 * Pops the a dialog holding this SpeechOptionPane 
 	 *  
 	 * @param parent the parent component of the dialog
-	 * @param the {@code Object} to display
+	 * @param message the {@code Object} to display
 	 * @return an integer indicating the option selected by the user
 	 */
 	@SuppressWarnings("serial")
@@ -121,12 +119,13 @@
 		optPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL,InputEvent.CTRL_DOWN_MASK),"shut_up");
 		optPane.getActionMap().put("shut_up", SpeechUtilities.getShutUpAction());
 		final JDialog dialog = optPane.createDialog(parent, title);
+		dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
 		SpeechUtilities.changeTabListener(optPane,dialog);
 		/* when either button is pressed, dialog is disposed and the button itself becomes the optPane.value */
 		ActionListener buttonListener = new ActionListener(){
 			@Override
 			public void actionPerformed(ActionEvent evt) {
-				onClose(dialog,message,(JButton)evt.getSource());
+				onClose(dialog,(JButton)evt.getSource());
 			}
 		};
 		okButton.addActionListener(buttonListener);
@@ -135,7 +134,6 @@
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
-		dialog.dispose();
 		if(okButton.equals(optPane.getValue())){
 			return OK_OPTION;
 		}else{
@@ -146,7 +144,7 @@
 	/**
 	 * Sets the string appearing at the top of the dialog where this option pane is displayed when {@code showDialog}
 	 * is called.
-	 * @param title
+	 * @param title the title of this option pane 
 	 */
 	public void setDialogTitle(String title){
 		this.title = title;
@@ -173,17 +171,16 @@
 	}
 	
 	/**
-	 * This method is called just after the user pressed either button of the dialog displayed
+	 * This method is called just after the user presses either button of the dialog displayed
 	 * after {@code showDialog} is called.
 	 * It assign a value to the return value and it frees the dialog resources.
 	 * It can be overwritten by subclasses but care should be taken of calling this class method via
 	 * {@code super} in order to properly close the dialog.   
 	 * 
 	 * @param dialog the dialog displayed after {@code showDialog} is called. 
-	 * @param message
 	 * @param source the button that triggered the closing of {@code dialog}
 	 */
-	protected void onClose(JDialog dialog,Object message, JButton source){
+	protected void onClose(JDialog dialog, JButton source){
 		optPane.setValue(source);
 		dialog.dispose();
 	}
@@ -196,13 +193,31 @@
 	
 	/* -------- STATIC METHODS ----------- */
 	
-	public static String showTextAreaDialog(Component parentComponent, String message, String initialSelectionValue){
+	/**
+	 * Shows a dialog with a text area requesting input for the user.
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message a displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param text the initial text the text area contains when the dialog is displayed 
+	 *  
+	 * @return the new text entered by the user
+	 */
+	public static String showTextAreaDialog(Component parentComponent, String message, String text){
 		JTextArea textArea = new JTextArea(NOTES_TEXT_AREA_ROW_SIZE,NOTES_TEXT_AREA_COL_SIZE);
-		textArea.setText(initialSelectionValue);
+		textArea.setText(text);
 		NarratorFactory.getInstance().speak(message);
 		return textComponentDialog(parentComponent, message, textArea);
 	}
 	
+	/**
+	 * Shows a dialog with a text field requesting input from the user 
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message a message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param initialSelectionValue the initial text the text field contains when the dialog is displayed 
+	 * 
+	 * @return the text entered by the user 
+	 */
 	public static String showInputDialog(Component parentComponent, String message, String initialSelectionValue){
 		final JTextField textField = new JTextField(initialSelectionValue);
 		textField.selectAll();
@@ -240,6 +255,7 @@
 		// start the editing sound
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		
 		if(optPane.getValue() == null)//window closed
@@ -251,10 +267,15 @@
 		}
 	}	
 	
-	public static String showInputDialog(Component parentComponent, String message){
-		return showInputDialog(parentComponent, message, "");
-	}
-	
+	/**
+	 * Shows a dialog with a {@code JComboBox} requesting selection from the user 
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message a message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param options options for the {@code JComboBox}
+	 * @param initialValue the options value selected when the dialog is shown   
+	 * @return the option selected by the user 
+	 */
 	public static Object showSelectionDialog(Component parentComponent, String message, Object[] options, Object initialValue){
 		final LoopComboBox comboBox = new LoopComboBox(options);
 		comboBox.setSelectedItem(initialValue);
@@ -282,6 +303,7 @@
 		// start the editing sound
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		if(optPane.getValue() == null)//window closed
 			return null;
@@ -292,6 +314,17 @@
 		}
 	}
 	
+	/**
+	 * Shows the dialog with a {@code JSpinner} requesting the selection of the speech rate 
+	 * of the main voice of the {@code Narrator} 
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message a message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param value the initial value 
+	 * @param min the minimum value of the spinner 
+	 * @param max the maximum value of the spinner 
+	 * @return the selected integer value or {@code null} if the user cancels the dialog  
+	 */
 	public static Integer showNarratorRateDialog(Component parentComponent, String message, int value, int min, int max){	
 		NarratorFactory.getInstance().speak(message);
 		final JSpinner spinner = new JSpinner(new LoopSpinnerNumberModel(value,min,max));
@@ -329,6 +362,7 @@
 		// start the editing sound
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		
 		/* set the speech rate back to the value passed as argument */
@@ -342,6 +376,14 @@
 		}
 	}
 	
+	/**
+	 * Brings up a dialog with selected options requesting user to confirmation.
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message a message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param optionType  an integer designating the options available on the dialog
+	 * @return an integer indicating the option selected by the user 
+	 */
 	public static int showConfirmDialog(Component parentComponent, String message, int optionType){
 		NarratorFactory.getInstance().speak(message);
 		JOptionPane optPane = new JOptionPane();
@@ -357,6 +399,7 @@
 		
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		
 		if(optPane.getValue() == null)//window closed
@@ -365,6 +408,13 @@
 			return ((Integer)optPane.getValue()).intValue();
 	}
 	
+	/**
+	 * Displays a message to the user.
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message the message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param messageType the type of message to be displayed
+	 */
 	public static void showMessageDialog(Component parentComponent, String message, int messageType){
 		NarratorFactory.getInstance().speak(MessageFormat.format(resources.getString("dialog.speech_option_pane.message"), message));
 		JOptionPane optPane = new JOptionPane();
@@ -378,13 +428,34 @@
 		SpeechUtilities.changeTabListener(optPane,dialog);
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 	}
 	
+	/**
+	 * Displays an error message to the user.
+	 * 
+	 * @param parentComponent the parent {@code Component} for the dialog
+	 * @param message the message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 */
 	public static void showMessageDialog(Component parentComponent, String message){
 		showMessageDialog(parentComponent,message,ERROR_MESSAGE);
 	}
 	
+	/**
+	 * Execute a ProgressDialogWorker task and 
+	 * shows an indeterminate progress bar dialog if the task is not completed after 
+	 * {@code millisToDecideToPopup}. The user can use the dialog <i>cancel</i> button 
+	 * to cancel the task. 
+	 * 
+	 * @param <T>  the result type returned by the worker 
+	 * @param <V>  the intermediate result type of the worker 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message message a message displayed in the dialog, such text is also uttered by the {@code Narrator} 
+	 * @param worker a {@code ProgressDialogWorker} that is executed when this method is called
+	 * @param millisToDecideToPopup the millisecond to let to the worker before popping the dialog up   
+	 * @return an integer indicating whether the task was completed or it was interrupted by the user 
+	 */
 	public static <T,V> int showProgressDialog(Component parentComponent, String message,final ProgressDialogWorker<T,V> worker, int millisToDecideToPopup){
 		JProgressBar progressBar = worker.bar;
 		Object displayObjects[] = {message, progressBar};
@@ -421,6 +492,15 @@
 		return OK_OPTION;
 	}
 	
+	/**
+	 * Shows a check box dialog to select the modifiers of a given property 
+	 * 
+	 * @param parentComponent  the parent {@code Component} for the dialog
+	 * @param message message a message displayed in the dialog, such text is also uttered by the {@code Narrator}
+	 * @param modifierTypes the different types of modifiers that are available for a given property
+	 * @param modifierIndexes the initial selection of modifiers as the dialog is shown
+	 * @return a new set with the modifier indexes selected by the user 
+	 */
 	public static Set<Integer> showModifiersDialog(Component parentComponent, String message, List<String> modifierTypes, Set<Integer> modifierIndexes){
 		JOptionPane optPane = new JOptionPane();
 		
@@ -454,6 +534,7 @@
 		
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		
 		if(optPane.getValue() == null)//window closed
@@ -469,6 +550,12 @@
 		}
 	}
 	
+	/**
+	 * Returns the specified component's {code Frame}.
+	 * 
+	 * @param parentComponent the component for this dialog 
+	 * @return the {@code Frame} that contains the component 
+	 */
 	public static Frame getFrameForComponent(Component parentComponent){
 		return JOptionPane.getFrameForComponent(parentComponent);
 	}
@@ -492,8 +579,17 @@
 	public static final int YES_OPTION = JOptionPane.YES_OPTION;
 	public static final int NO_OPTION = JOptionPane.NO_OPTION;
 	
-	
+	/**
+	 * A swing worker to be passed as argument to {@code showProgressDialog}. The {@code execute}
+	 * method can be interrupted by the user by clicking on the {@code cancel} button of the dialog. 
+	 *
+	 * @param <T>  the result type returned by this {@code SwingWorker}'s {@code doInBackground} and {@code get} methods
+	 * @param <V>  the type used for carrying out intermediate results by this {@code SwingWorker}'s {@code publish} and {@code process} methods
+	 */
 	public static abstract class ProgressDialogWorker<T,V> extends SwingWorker<T,V> {
+		/**
+		 * Creates a new ProgressDialogWorker 
+		 */
 		public ProgressDialogWorker(){
 			bar = new JProgressBar();
 			bar.setIndeterminate(true);
@@ -510,7 +606,7 @@
 		}
 		
 		private JDialog dialog;
-		protected JProgressBar bar;
+		private JProgressBar bar;
 	}
 	
 	
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechSummaryPane.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/SpeechSummaryPane.java	Tue Jul 10 22:39:37 2012 +0100
@@ -43,14 +43,24 @@
 /**
  * Abstract class with an one-line call to display a summary dialog.
  * The summary text as well as focused components are spoken out through text to speech 
- * synthesis performed by the {@link Narrator} instance.  
- * A summary dialog has non editable text field and a button
- * for confirmation only.  
+ * synthesis performed by the {@code Narrator} instance.  
+ * A summary dialog has non editable text field and a button for confirmation only.  
  * 
  *
  */
 public abstract class SpeechSummaryPane {
 	
+	/**
+	 * shows the summary dialog 
+	 * @param parentComponent determines the {@code Frame} in which the dialog is displayed
+	 * @param title the title of the displayed dialog
+	 * @param text the text to be displayed in this dialog. his text, together with the title 
+	 *   is uttered through the {@code Narrator} as soon as the dialog is shown  
+	 * @param optionType  an integer designating the options available on the dialog 
+	 *  either {@code OK_CANCEL_OPTION} or {@code OK_OPTION} 
+	 * @param options an array of strings indicating the possible choices the user can make 
+	 * @return an integer indicating the option selected by the user. Either {@code OK} or {@code CANCEL}
+	 */
 	public static int showDialog(Component parentComponent, String title, String text, int optionType, String[] options){
 		if(optionType == OK_CANCEL_OPTION && options.length < 2)
 			throw new IllegalArgumentException("option type and opions number must be consistent");
@@ -98,6 +108,7 @@
 		// start the editing sound
 		SoundFactory.getInstance().startLoop(SoundEvent.EDITING);
 		dialog.setVisible(true);
+		dialog.dispose();
 		SoundFactory.getInstance().stopLoop(SoundEvent.EDITING);
 		NarratorFactory.getInstance().shutUp();
 		
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/TemplateEditor.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/TemplateEditor.java	Tue Jul 10 22:39:37 2012 +0100
@@ -25,7 +25,7 @@
 /**
  * A template editor is used to create new types of diagrams. 
  * 
- * Template editors are run in the Event Dispatching Thread and can therefore make use of swimg components
+ * Template editors are run in the Event Dispatching Thread and can therefore make use of <i>Swing</i> components
  * to prompt the user with choices about the diagram to be created. The diagram created by 
  * a template editor is precisely a prototype.
  * Such prototypes diagrams will then be used 
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessFilter.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessFilter.java	Tue Jul 10 22:39:37 2012 +0100
@@ -40,9 +40,22 @@
 import uk.ac.qmul.eecs.ccmi.speech.TreeSonifier;
 import uk.ac.qmul.eecs.ccmi.utils.GridBagUtilities;
 
+/**
+ * Base class for an awareness filter. It provides basic functionalities 
+ * for properties handling (save to a file, show edit dialog) 
+ */
 public abstract class AwarenessFilter {
-	
-	AwarenessFilter(String propertiesFilePath, String propertiesFileName) throws IOException{
+	/**
+	 * The constructor to be called by sub classes of this class. The filter is built
+	 * from a {@code SetProperties} previously saved on a file. If the file doesn't exist 
+	 * a new empty {@code SetProperties} is created.
+	 * 
+	 * @param propertiesFilePath the path to  the directory where the properties file is located. 
+	 * @param propertiesFileName the name of the properties file 
+	 * 
+	 * @throws IOException if I/O problems occurred when retrieving the properties file 
+	 */
+	protected AwarenessFilter(String propertiesFilePath, String propertiesFileName) throws IOException{
 		if(propertiesFilePath == null)
 			throw new IOException(ResourceBundle.getBundle(AwarenessFilter.class.getName()).getString("error.no_properties_dir"));
 		properties = new SetProperties();
@@ -53,19 +66,37 @@
 	
 	/**
 	 * Returns the file name this instance of AwarenessFilter was build from. It should return an existing 
-	 * XML file name, which will be read through {@code getClass().getResourceAsStream}, representing the tree
+	 * XML file name, which will be read through {@code getClass().getResourceAsStream()}, representing the tree
 	 * displayed after calling {@code showDialog}.
 	 * 
-	 * @see {@link uk.ac.qmul.eecs.ccmi.checkboxtree.CheckBoxTree}
+	 * @see uk.ac.qmul.eecs.ccmi.checkboxtree.CheckBoxTree
 	 * 
 	 * @return the name of the XML file  
 	 */
 	protected abstract String getXMLFileName();
 	
+	/**
+	 * Returns the title of the dialog displayed when {@link #showDialog(Component)}
+	 * is called.
+	 * 
+	 * @return a title for the preferences dialog
+	 */
 	protected abstract String getDialogTitle();
 	
+	/**
+	 * Saves the properties with a custom comment. Subclasses must implement 
+	 * this method providing the custom comment.  
+	 * 
+	 * @param parentComponent the parent component of the displayed dialog
+	 */
 	public abstract void saveProperties(Component parentComponent);
 	
+	/**
+	 * Brings up a dialog with a {@code CheckBoxTree} that can be used to set the properties of 
+	 * this filter. Once the user presses the OK button, the selected properties are saved.
+	 * 
+	 * @param parent the parent component of the displayed dialog 
+	 */
 	public void showDialog(Component parent){
 		/* create and init components */
 		InputStream in = getClass().getResourceAsStream(getXMLFileName());
@@ -136,7 +167,14 @@
 		}
 	}
 	
-	public void saveProperties(Component parentComponent, String comments) {
+	/**
+	 * Saves the properties to a file 
+	 * 
+	 * @param parentComponent a parent component where a message dialog will be displayed 
+	 *  if an I/O error occur when saving the file    
+	 * @param comments additional comments to add at the beginning of the file.
+	 */
+	protected void saveProperties(Component parentComponent, String comments) {
 		ResourceBundle resources = ResourceBundle.getBundle(AwarenessFilter.class.getName());
 		try {
 			if(!propertiesFile.getParentFile().exists())
@@ -152,6 +190,10 @@
 		}
 	}
 	
+	/**
+	 * An instance of {@code SetProperties} where the user configuration 
+	 * is saved.  
+	 */
 	protected final SetProperties properties;
 	private boolean configurationHasChanged;
 	private File propertiesFile; 
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanel.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanel.java	Tue Jul 10 22:39:37 2012 +0100
@@ -43,14 +43,31 @@
 		this.diagramName = diagramName;
 	}
 	
-	public AwarenessTextPane getUsersPane() {
+	/**
+	 * Returns a reference to the bottom pane where the name of the users
+	 * is displayed
+	 * 
+	 * @return an awareness text pane 
+	 */
+	AwarenessTextPane getUsersPane() {
 		return usersPane;
 	}
 
-	public AwarenessTextPane getRecordsPane() {
+	/**
+	 * Returns a reference to the top pane where the name of the users
+	 * is displayed
+	 * 
+	 * @return  an awareness text pane 
+	 */
+	AwarenessTextPane getRecordsPane() {
 		return recordsPane;
 	}
 	
+	/**
+	 * Returns the name of the diagram this panel is bound to 
+	 * 
+	 * @return the name of the diagram this panel is bound to.
+	 */
 	public String getDiagramName(){
 		return diagramName;
 	}
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanelEditor.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessPanelEditor.java	Tue Jul 10 22:39:37 2012 +0100
@@ -29,14 +29,27 @@
 import uk.ac.qmul.eecs.ccmi.network.AwarenessMessage;
 
 public class AwarenessPanelEditor {
+	/**
+	 * Creates a new editor 
+	 */
 	public AwarenessPanelEditor() {
 		awarenessPanels = Collections.synchronizedList(new ArrayList<AwarenessPanel>());
 	}
 
+	/**
+	 * Adds a new panel to the editor. {@code getDiagramName} of {@code panel}
+	 * will be used to retrieve the panel when a change is done to it.  
+	 *  
+	 * @param panel the panel to add
+	 */
 	public void addAwarenessPanel(AwarenessPanel panel){
 		awarenessPanels.add(panel);
 	}
 	
+	/**
+	 * Removes a panel from the editor 
+	 * @param panel the panel to remove
+	 */
 	public void removeAwarenessPanel(AwarenessPanel panel){
 		awarenessPanels.remove(panel);
 	}
@@ -44,7 +57,8 @@
 	/**
 	 * Replaces a user's user name with a new one. 
 	 * 
-	 * @param diagramName the diagram the update has to be performed on
+	 * @param diagramName the name of the diagram linked to the panel 
+	 *  the update has to be performed on
 	 * @param userNames a concatenation of the new user name and the old one. The
 	 * old user name can possibly be the empty string if the client has sent its 
 	 * user name for the first time (hence no old user name exists). 
@@ -64,12 +78,25 @@
 		panel.getUsersPane().insert(names[0]+'\n');
 	}
 	
+	/**
+	 * Removes a user name
+	 * @param diagramName the name of the diagram linked to the panel 
+	 *  the update has to be performed on
+	 * @param userName the name to be removed 
+	 */
 	public void removeUserName(String diagramName, String userName){
 		AwarenessPanel panel = findPanel(diagramName);
 		if(panel != null)
 			panel.getUsersPane().remove(userName+'\n');
 	}
 	
+	/**
+	 * Adds a new record to the panel 
+	 * 
+	 * @param diagramName the name of diagram linked to the panel the record 
+	 *  has to be added to
+	 * @param record the record to add 
+	 */
 	public void addRecord(String diagramName, String record){
 		AwarenessPanel panel = findPanel(diagramName);
 		if(panel == null)
@@ -77,6 +104,12 @@
 		panel.getRecordsPane().insert(record);
 	}
 	
+	/**
+	 * Adds a record that will automatically be removed after TIMER_DELAY millisecond
+	 * @param diagramName the name of diagram linked to the panel the record 
+	 *  has to be added to
+	 * @param record  the record to add 
+	 */
 	public void addTimedRecord(final String diagramName, final String record){
 		addRecord(diagramName, record);
 		Timer timer = new Timer(TIMER_DELAY,new ActionListener(){
@@ -89,6 +122,13 @@
 		timer.start();
 	}
 	
+	/**
+	 * Removes a record from the panel 
+	 * 
+	 * @param diagramName the name of diagram linked to the panel the record 
+	 *  has to be removed from
+	 * @param record the record to add 
+	 */
 	public void removeRecord(String diagramName, String record){
 		AwarenessPanel panel = findPanel(diagramName);
 		if(panel == null)
@@ -96,6 +136,11 @@
 		panel.getRecordsPane().remove(record);
 	}
 	
+	/**
+	 * Removes all the record from a panel 
+	 * @param diagramName the name of diagram linked to the panel the records
+	 *  have to be removed from
+	 */
 	public void clearRecords(String diagramName){
 		AwarenessPanel panel = findPanel(diagramName);
 		if(panel == null)
@@ -116,5 +161,8 @@
 	}
 	
 	private List<AwarenessPanel> awarenessPanels;
-	private static int TIMER_DELAY = 2000;
+	/**
+	 * The time (in milliseconds) a {@code TimedRecord} will stay in the panel 
+	 */
+	public static final int TIMER_DELAY = 2000;
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessTextPane.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/AwarenessTextPane.java	Tue Jul 10 22:39:37 2012 +0100
@@ -32,8 +32,8 @@
 import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities;
 
 @SuppressWarnings("serial")
-public class AwarenessTextPane extends JTextPane {
-	public AwarenessTextPane(String accessibleName){
+class AwarenessTextPane extends JTextPane {
+	AwarenessTextPane(String accessibleName){
 		records = new LinkedList<String>();
 		addKeyListener(SpeechUtilities.getSpeechKeyListener(false,true));
 		/* prevents getText() from automatically turn all the \n to \r\n */
@@ -43,17 +43,17 @@
 		SpeechUtilities.changeTabListener(this,DiagramEditorApp.getFrame());
 	}
 	
-	public void insert(String record){
+	void insert(String record){
 		records.add(0, record);
 		update();
 	}
 	
-	public void remove(String record){
+	void remove(String record){
 		records.remove(record);
 		update();
 	}
 	
-	public void clear(){
+	void clear(){
 		records.clear();
 		update();
 	}
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/BroadcastFilter.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/BroadcastFilter.java	Tue Jul 10 22:39:37 2012 +0100
@@ -27,13 +27,32 @@
 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource;
 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
 
+/**
+ * A filter for {@code DiagramEventActionSource} generated by a client and to be broadcasted 
+ * by the server to the other connected clients. The user (running the server) can select on a dialog
+ * the cases in which the {@code DiagramEventActionSource} is to be broadcasted or not.
+ * User selections are saved persistently using the persistence functionalities of {@code SetProperties}.
+ * 
+ * @see uk.ac.qmul.eecs.ccmi.checkboxtree.SetProperties
+ */
 public class BroadcastFilter extends AwarenessFilter  {
-	public static BroadcastFilter getInstance(){
+	/**
+	 * Creates a new instance of this class. Successive calls on this method 
+	 * will create a new filter, which will be returned by {@code getInstance} from then on 
+	 *   
+	 * @return the instance created 
+	 * @throws IOException if I/O problems occurred when retrieving the properties file 
+	 */
+	public static BroadcastFilter createInstance() throws IOException {
+		broadcastFilter = new BroadcastFilter();
 		return broadcastFilter;
 	}
 	
-	public static BroadcastFilter createInstance() throws IOException {
-		broadcastFilter = new BroadcastFilter();
+	/**
+	 * Returns the instance created by the last call of {@code createInstance} 
+	 * @return the instance of this class 
+	 */
+	public static BroadcastFilter getInstance(){
 		return broadcastFilter;
 	}
 	
@@ -43,12 +62,12 @@
 	}
 	
 	@Override
-	protected String getXMLFileName(){
+	protected final String getXMLFileName(){
 		return "BroadcastFilterTree.xml";
 	}
 	
 	@Override
-	protected String getDialogTitle(){
+	protected final String getDialogTitle(){
 		return ResourceBundle.getBundle(AwarenessFilter.class.getName()).getString("dialog.broadcast.title");
 	}
 	
@@ -61,6 +80,14 @@
 			);
 	}
 	
+	/**
+	 * Accept or refuse for broadcasting an action generated remotely by a client. 
+	 * The local user can determine which actions must be filtered out via the dialog
+	 * displayed by {@code showDialog}.
+	 * 
+	 * @param action the action to filter 
+	 * @return whether the action can be broadcasted to other client
+	 */
 	public boolean accept(DiagramEventActionSource action){
 		/* don't accept if the user didn't select the modality this action was generated from */
 		switch(action.type){
@@ -110,6 +137,14 @@
 		return true;
 	}
 	
+	/**
+	 * Process for broadcasting an action generated remotely by a client. 
+	 * The local user can determine which parts of the action can be excluded 
+	 * via the dialog displayed by {@code showDialog}.
+	 * 
+	 * @param action the action to filter 
+	 * @return whether the action can be broadcasted to other client
+	 */
 	public DiagramEventActionSource process(DiagramEventActionSource action){
 		/* delete the timestamp if the user decided not to broadcast it */
 		if(properties.contains("Broadcast Filter.When.Active History"))
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/DisplayFilter.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/awareness/DisplayFilter.java	Tue Jul 10 22:39:37 2012 +0100
@@ -28,14 +28,34 @@
 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource;
 import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
 
-
+/**
+ * A filter for {@code DiagramEventActionSource} generated remotely and to be displayed 
+ * both visually and via a text to speech sinthesizer. The user can select on a dialog
+ * the cases in which the {@code DiagramEventActionSource} is to be displayed or not.
+ * User selections are saved persistently using the persistence functionalities of {@code SetProperties}.
+ * 
+ * @see uk.ac.qmul.eecs.ccmi.checkboxtree.SetProperties
+ */
 public class DisplayFilter extends AwarenessFilter {
+	/**
+	 * Creates a new instance of this class. Successive calls on this method will return 
+	 * the filter already created rather than creating anew one. 
+	 *   
+	 * @return the instance created 
+	 * @throws IOException if I/O problems occurred when retrieving the properties file 
+	 */
 	public static DisplayFilter createInstance() throws IOException{
 		if(displayFilter == null)
 			displayFilter = new DisplayFilter();
 		return displayFilter;
 	}
 	
+	/**
+	 * Returns the unique instance of this class. 
+	 *  
+	 * @return the unique instance of this class or {@code null} if {@code createInstance} has never
+	 * been called before.  
+	 */
 	public static DisplayFilter getInstance(){
 		return displayFilter;
 	}
@@ -47,12 +67,12 @@
 	}
 
 	@Override
-	protected String getXMLFileName() {
+	protected final String getXMLFileName() {
 		return "DisplayFilterTree.xml";
 	}
 	
 	@Override
-	protected String getDialogTitle(){
+	protected final String getDialogTitle(){
 		return resources.getString("dialog.display.title");
 	}
 	
@@ -61,10 +81,26 @@
 		super.saveProperties(parentComponent, resources.getString("display.properties.comments"));
 	}
 	
+	/**
+	 * Returns a string to be uttered by the narrator starting from the action 
+	 * that the user is to be made aware of 
+	 * 
+	 * @param actionSource the action source describing a remote action that the local
+	 *  user has to be made aware of 
+	 * @return a string to be uttered by a {@code Narrator}
+	 */
 	public String processForSpeech(DiagramEventActionSource actionSource){
 		return process(actionSource,"Speech");
 	}
 	
+	/**
+	 * Returns a string to be inserted on the awareness panel starting from the action 
+	 * that the user is to be made aware of 
+	 * 
+	 * @param actionSource the action source describing a remote action that the local
+	 *  user has to be made aware of 
+	 * @return a string to be inserted in the awareness panel 
+	 */
 	public String processForText(DiagramEventActionSource actionSource){
 		return process(actionSource,"Text");
 	}
@@ -153,7 +189,7 @@
 	}
 	
 	private static DisplayFilter displayFilter;
-	ResourceBundle resources;
+	private ResourceBundle resources;
 }
 
 
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/filechooser/SpeechFileChooser.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/filechooser/SpeechFileChooser.java	Tue Jul 10 22:39:37 2012 +0100
@@ -157,14 +157,14 @@
 		 * been entered by the user. If not, the dialog won't close and a error will be notified through the narrator  */
 		SpeechOptionPane optionPane = new SpeechOptionPane(resources.getString("dialog.open.title")){
 			@Override
-			protected void onClose(JDialog dialog,Object message, JButton source){
+			protected void onClose(JDialog dialog, JButton source){
 				if(source.equals(getOkButton())){
 					if(fileNameTextField.getText().isEmpty()){
 						NarratorFactory.getInstance().speak(resources.getString("dialog.error.no_file_name"));
 						return;
 					}
 				}
-				super.onClose(dialog, message, source);
+				super.onClose(dialog, source);
 			}
 		};
 		
--- a/java/src/uk/ac/qmul/eecs/ccmi/gui/persistence/PersistenceManager.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/gui/persistence/PersistenceManager.java	Tue Jul 10 22:39:37 2012 +0100
@@ -61,9 +61,10 @@
 import uk.ac.qmul.eecs.ccmi.utils.CharEscaper;
 
 /**
- * The PersistanceManager provides methods saving and retrieving diagrams from an XML
+ * The PersistanceManager provides methods for saving and retrieving diagrams from an XML
  * file. Both templates diagrams (prototypes from which actual diagram instances are created
- * through cloning) and diagram instances can be saved to a file. 
+ * through cloning) and diagram instances can be saved to a file. The tag name used in the XML 
+ * file can be accessed via the static {@code String} variables of this class.  
  * 
  */
 public abstract class PersistenceManager {
@@ -121,6 +122,8 @@
 	 * 
 	 * @param XMLFile the file to read the diagram from 
 	 * @throws IOException if there are any I/O problems with the file 
+	 * 
+	 * @return the diagram encoded in {@code XMLFile}
 	 */
 	public static Diagram decodeDiagramTemplate(File XMLFile) throws IOException{
 		ResourceBundle resources = ResourceBundle.getBundle(PersistenceManager.class.getName());
@@ -163,12 +166,13 @@
 	
 	/**
 	 * Encodes a diagram instance into the given output stream. Using output stream
-	 * instead of Writer as it's advised by the StreamResult API 
-	 * @see http://download.oracle.com/javase/6/docs/api/javax/xml/transform/stream/StreamResult.html#setOutputStream(java.io.OutputStream)
+	 * instead of {@code Writer} as it's advised by the <i>StreamResult API</i> 
+	 * @see http://download.oracle.com/javase/6/docs/api/javax/xml/transform/stream/StreamResult.html
 	 * 
-	 * @param diagram : the diagram to encode
-	 * @param out : where the diagram will be encoded
-	 * @throws IOException
+	 * @param diagram the diagram to encode
+	 * @param newName the new name of the diagram to encode. This will also be the name of the file 
+	 * @param out where the diagram will be encoded
+	 * @throws IOException if there are any I/O problems with the file 
 	 */
 	public static void encodeDiagramInstance(Diagram diagram, String newName, OutputStream out) throws IOException{
 		ResourceBundle resources = ResourceBundle.getBundle(PersistenceManager.class.getName());
@@ -277,10 +281,29 @@
 			diagram.setName(newName);
 	}
 	
+	/**
+	 * Encodes a diagram instance into the given output stream. Using output stream
+	 * instead of {@code Writer} as it's advised by the <i>StreamResult API</i> 
+	 * @see http://download.oracle.com/javase/6/docs/api/javax/xml/transform/stream/StreamResult.html
+	 * 
+	 * @param diagram the diagram to encode
+	 * @param out an output stram to the file where the diagram will be encoded
+	 * @throws IOException if there are any I/O problems with the file 
+	 */
 	public static void encodeDiagramInstance(Diagram diagram, OutputStream out) throws IOException{
 		encodeDiagramInstance(diagram,null,out);
 	}
 	
+	/**
+	 * Decodes a diagram instance from the given input stream. Using input stream
+	 * instead of {@code Reader} as it's advised by the <i>StreamResult API</i> 
+	 * @see http://download.oracle.com/javase/6/docs/api/javax/xml/transform/stream/StreamResult.html
+	 * 
+	 * @param in an input stream to the file the diagram is decoded from 
+	 * @throws IOException if there are any I/O problems with the file 
+	 * 
+	 * @return the diagram encoded in the file 
+	 */
 	public static Diagram decodeDiagramInstance(InputStream in) throws IOException {
 		ResourceBundle resources = ResourceBundle.getBundle(PersistenceManager.class.getName());
 		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/DummyHaptics.java	Tue May 29 15:32:19 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*  
- CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
-  
- 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.haptics;
-
-import java.awt.geom.Line2D;
-import java.util.BitSet;
-
-/*
- * A dummy implementation of the Haptics interface. All its methods are empty,
- * so every call will have no effect whatsoever.
- */
-class DummyHaptics implements Haptics {
-	
-	public DummyHaptics(){}
-
-	@Override
-	public int init(int width, int height) { return 0;}
-
-	@Override
-	public synchronized void addNode(double x, double y, int nodeHashCode, String diagramName) {}
-
-	@Override
-	public synchronized void removeNode(int nodeHashCode, String diagramName) {}
-
-	@Override
-	public synchronized void removeEdge(int nodeHashCode, String diagramName){}
-
-	@Override
-	public synchronized void dispose() {}
-
-	@Override
-	public void addNewDiagram(String diagramName, boolean switchAfter) {}
-
-	@Override
-	public synchronized void switchDiagram(String diagramName) {	}
-
-	@Override
-	public synchronized void removeDiagram(String diagramNameToRemove, String diagramNameNext) {}
-
-	@Override
-	public synchronized void moveNode(double x, double y, int nodeHashCode, String diagramName) {}
-
-	@Override
-	public synchronized void addEdge(int nodeHashCode, double[] xs, double[] ys,
-			BitSet[] adjMatrix, int nodeStart, int stipplePattern, Line2D attractLine, String diagramName) {}
-
-	@Override
-	public synchronized void updateEdge(int nodeHashCode, double[] xs,
-			double[] ys, BitSet[] adjMatrix, int nodeStart, Line2D attractLine, String diagramName) {}
-
-	@Override
-	public synchronized void attractTo(int elementHashCode) {}
-	
-	@Override
-	public void pickUp(int elementHashCode){}
-	
-	@Override
-	public boolean isAlive(){
-		return false;
-	}
-
-	@Override
-	public void run() {}
-}
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/Edge.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/Edge.java	Tue Jul 10 22:39:37 2012 +0100
@@ -50,5 +50,9 @@
 	public double attractPointX;
 	public double attractPointY;
 	public int nodeStart;
+	
+	static final int DOTTED_LINE = 0xF0F0;
+	static final int DASHED_LINE = 0xAAAA;
+	static final int SOLID_LINE = 0xFFFF;
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/Empties.java	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,30 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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.haptics;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+class Empties {
+	public static final ArrayList<Node> EMPTY_NODE_LIST = new ArrayList<Node>(0);
+	public static final ArrayList<Edge> EMPTY_EDGE_LIST = new ArrayList<Edge>(0);
+	public static final HashMap<Integer,Node> EMPTY_NODE_MAP = new HashMap<Integer,Node>();
+	public static final HashMap<Integer,Edge> EMPTY_EDGE_MAP = new HashMap<Integer,Edge>();
+}
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.dll has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/FalconHaptics.java	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,300 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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.haptics;
+
+import java.awt.Dimension;
+import java.awt.Toolkit;
+import java.awt.geom.Line2D;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.ListIterator;
+
+import uk.ac.qmul.eecs.ccmi.utils.OsDetector;
+import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
+import uk.ac.qmul.eecs.ccmi.utils.ResourceFileWriter;
+
+class FalconHaptics extends Thread implements Haptics {
+	static Haptics createInstance(HapticListenerThread listener) {
+		if(OsDetector.isWindows()){
+			/* create a directory for the dll distributed with HAPI library */
+			String libDir = PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir"));
+			File hapiDir = new File(libDir,"HAPI");
+			hapiDir.mkdir();
+			
+			/* try to load .dll's. First copy it in the home/ccmi_editor_data/lib directory  */
+			String[] dlls = {"HAPI/pthreadVC2.dll","HAPI/FreeImage.dll","HAPI/freeglut.dll",
+					"HAPI/H3DUtil_vc9.dll","HAPI/HAPI_vc9.dll","FalconHaptics.dll"}; 
+			ResourceFileWriter fileWriter = new ResourceFileWriter();
+			for(String dll : dlls){
+				URL url = OmniHaptics.class.getResource(dll);
+				fileWriter.setResource(url);
+				fileWriter.writeOnDisk(libDir, dll);
+				String path = fileWriter.getFilePath();
+				try{
+					if(path == null)
+						throw new UnsatisfiedLinkError(dll+" missing");
+					System.load( path );
+				}catch(UnsatisfiedLinkError e){
+					System.err.println(e.getMessage());
+					e.printStackTrace();
+					return null; 
+				}
+			}
+		}else{
+			return null;
+		}
+		
+		FalconHaptics falcon = new FalconHaptics("FalconHaptics");
+		falcon.hapticListener = listener;
+		/* start up the listener which immediately stops, waiting for commands */
+		if(!falcon.hapticListener.isAlive())
+			falcon.hapticListener.start();
+		/* start up the haptics thread which issues commands from the java to the c++ thread */
+		falcon.start();
+		synchronized(falcon){
+			try {
+				falcon.wait();
+			}catch (InterruptedException ie) {
+				throw new RuntimeException(ie); // must never happen 
+			}
+		}
+		if(falcon.initFailed)
+			return null;
+		else 
+			return falcon;
+	}
+	
+	private FalconHaptics(String threadName){
+		super(threadName);
+		
+		nodes = new HashMap<String,ArrayList<Node>>();
+		edges = new HashMap<String,ArrayList<Edge>>();
+		currentNodes = Empties.EMPTY_NODE_LIST;
+		currentEdges = Empties.EMPTY_EDGE_LIST;
+		
+		attractTo = 0;
+	}
+	
+	private native int initFalcon(int width, int height) throws IOException ;
+	
+	@Override
+	public void addNewDiagram(String diagramName) {
+		ArrayList<Node> cNodes = new ArrayList<Node>(30);
+		ArrayList<Edge> cEdges = new ArrayList<Edge>(30);
+		nodes.put(diagramName, cNodes);
+		edges.put(diagramName, cEdges);
+		
+		synchronized(this){
+			currentNodes = cNodes;
+			currentEdges = cEdges;
+			collectionsChanged = true;
+		}
+	}
+
+	@Override
+	public synchronized void switchDiagram(String diagramName) {
+		if(!nodes.containsKey(diagramName))
+			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramName);
+		
+		currentNodes = nodes.get(diagramName);
+		currentEdges = edges.get(diagramName);
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void removeDiagram(String diagramNameToRemove,
+			String diagramNameOfNext) {
+		if(!nodes.containsKey(diagramNameToRemove))
+			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameToRemove);
+		
+		nodes.remove(diagramNameToRemove);
+		edges.remove(diagramNameToRemove);
+		
+		if(diagramNameOfNext == null){
+			currentNodes = Empties.EMPTY_NODE_LIST;	
+			currentEdges = Empties.EMPTY_EDGE_LIST;
+		}else {
+			if(!nodes.containsKey(diagramNameOfNext))
+				throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameOfNext);
+			currentNodes = nodes.get(diagramNameOfNext);
+			currentEdges = edges.get(diagramNameOfNext);
+		}
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void addNode(double x, double y, int nodeHashCode, String diagramName) {
+		Node n = new Node(x,y,nodeHashCode, nodeHashCode);
+		if(diagramName == null){
+			currentNodes.add(n);
+		}else{
+			nodes.get(diagramName).add(n);
+		}
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void removeNode(int nodeHashCode, String diagramName) {
+		ListIterator<Node> itr = (diagramName == null) ? currentNodes.listIterator() : nodes.get(diagramName).listIterator();
+		while(itr.hasNext()){
+			Node n = itr.next();
+			if(n.diagramId == nodeHashCode){
+				itr.remove();
+				collectionsChanged = true;
+				break;
+			}
+		}
+	}
+
+	@Override
+	public synchronized void moveNode(double x, double y, int nodeHashCode,
+			String diagramName) {
+		ArrayList<Node> iterationList = (diagramName == null) ? currentNodes : nodes.get(diagramName); 
+		for(Node n : iterationList){
+			if(n.diagramId == nodeHashCode){
+				n.x = x;
+				n.y = y;
+				collectionsChanged = true;
+				break;
+			}
+		}
+	}
+
+	@Override
+	public synchronized void addEdge(int edgeHashCode, double[] xs, double[] ys,
+			BitSet[] adjMatrix, int nodeStart, int stipplePattern,
+			Line2D attractLine, String diagramName) {
+		/* find the mid point of the line of attraction */
+		double pX = Math.min(attractLine.getX1(), attractLine.getX2());
+		double pY = Math.min(attractLine.getY1(), attractLine.getY2());
+		pX += Math.abs(attractLine.getX1() -  attractLine.getX2())/2;
+		pY += Math.abs(attractLine.getY1() -  attractLine.getY2())/2;
+		
+		Edge e = new Edge(edgeHashCode,edgeHashCode, xs, ys, adjMatrix, nodeStart, stipplePattern, pX, pY);
+		/* add the edge reference to the edges list */
+		if(diagramName == null){
+			currentEdges.add(e);
+		}else{
+			edges.get(diagramName).add(e);
+		}
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void updateEdge(int edgeHashCode, double[] xs, double[] ys,
+			BitSet[] adjMatrix, int nodeStart, Line2D attractLine,
+			String diagramName) {
+		
+		for(Edge e : currentEdges){
+			if(e.diagramId == edgeHashCode){
+				e.xs = xs;
+				e.ys = ys;
+				e.size = xs.length;
+				e.adjMatrix = adjMatrix;
+				e.nodeStart = nodeStart;
+				// find the mid point of the line of attraction
+				double pX = Math.min(attractLine.getX1(), attractLine.getX2());
+				double pY = Math.min(attractLine.getY1(), attractLine.getY2());
+				pX += Math.abs(attractLine.getX1() -  attractLine.getX2())/2;
+				pY += Math.abs(attractLine.getY1() -  attractLine.getY2())/2;
+				e.attractPointX = pX;
+				e.attractPointY = pY;
+			}
+		}
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void removeEdge(int edgeHashCode, String diagramName) {
+		ListIterator<Edge> itr = (diagramName == null) ? currentEdges.listIterator() : edges.get(diagramName).listIterator();
+		while(itr.hasNext()){
+			Edge e = itr.next();
+			if(e.diagramId == edgeHashCode){
+				itr.remove();
+				collectionsChanged = true;
+				break;
+			}
+		}
+		collectionsChanged = true;
+	}
+
+	@Override
+	public synchronized void attractTo(int elementHashCode) {
+		attractTo = elementHashCode;
+	}
+
+	@Override
+	public synchronized void pickUp(int elementHashCode) {
+		pickUp = true;
+	}
+
+	@Override
+	public void setVisible(boolean visible) {
+		// falcon haptics window cannot be made invisible 
+	}
+
+	@Override
+	public synchronized void dispose(){
+		shutdown = true;
+		/* wait for the haptic thread to shut down */
+		try {
+			wait();
+		} catch (InterruptedException e) {
+			throw new RuntimeException(e);
+		}
+	}
+	
+	@Override
+	public void run() {
+		/* get the screen size which will be passed to init methos in order to set up a window
+		 * for the haptic with the same size as the swing one
+		 */
+		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+
+		int screenWidth = (int)screenSize.getWidth();
+		int screenHeight = (int)screenSize.getHeight();
+		currentNodes.size();
+		try {
+			initFalcon(screenWidth * 5 / 8,screenHeight * 5 / 8);
+		} catch (IOException e) {
+			throw new RuntimeException();// OMNI haptic device doesn't cause any exception
+		}
+	}
+	
+	/* the diagram currently selected */
+	private ArrayList<Node> currentNodes;
+	private ArrayList<Edge> currentEdges;
+
+	/* maps with all the diagrams in the editor */
+	private HashMap<String,ArrayList<Node>> nodes;
+	private HashMap<String,ArrayList<Edge>> edges;
+	
+	private HapticListenerThread hapticListener;
+	private boolean initFailed;
+	private boolean collectionsChanged;
+	private boolean pickUp;
+	private int attractTo;
+	private boolean shutdown;
+}
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/FreeImage.dll has changed
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/H3DUtil_vc9.dll has changed
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/HAPI_vc9.dll has changed
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/freeglut.dll has changed
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/HAPI/pthreadVC2.dll has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListSupport.java	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,129 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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.haptics;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/* used by MouseHaptics */
+class HapticListSupport {
+	
+	public HapticListSupport(){
+		nodes = new HashMap<String,List<Node>>();
+		edges = new HashMap<String,List<Edge>>();
+		currentNodes = Empties.EMPTY_NODE_LIST;
+		currentEdges = Empties.EMPTY_EDGE_LIST;
+	}
+	
+	public void addNewDiagram(String diagramName) {
+		currentNodes = new ArrayList<Node>(30);
+		nodes.put(diagramName,currentNodes);
+		currentEdges = new ArrayList<Edge>(30);
+		edges.put(diagramName,currentEdges);
+	}
+	
+	public void switchDiagram(String diagramName) {
+		currentNodes = nodes.get(diagramName);
+		currentEdges = edges.get(diagramName);
+	}
+	
+	public void removeDiagram(String diagramNameToRemove,
+			String diagramNameOfNext) {
+		nodes.remove(diagramNameToRemove);
+		edges.remove(diagramNameToRemove);
+		currentNodes = nodes.get(diagramNameOfNext);
+		currentEdges = edges.get(diagramNameOfNext);
+		if(currentNodes == null){
+			currentNodes = Empties.EMPTY_NODE_LIST;
+			currentEdges = Empties.EMPTY_EDGE_LIST;
+		}
+	}
+	
+	public void addNode(Node n, String diagramName){
+		if(diagramName == null)
+			currentNodes.add(n);
+		else
+			nodes.get(diagramName).add(n);
+	}
+	
+	public Node removeNode(int nodeHashCode, String diagramName) {
+		List<Node> list = (diagramName == null) ? currentNodes : nodes.get(diagramName);
+		for(Node n : list){
+			if(n.diagramId == nodeHashCode){
+				list.remove(n);
+				return n;
+			}
+		}
+		return null;
+	}
+	
+	public Node getNode(int nodeHashCode, String diagramName){
+		List<Node> list = (diagramName == null) ? currentNodes : nodes.get(diagramName);
+		for(Node n : list){
+			if(n.diagramId == nodeHashCode){
+				return n;
+			}
+		}
+		return null;
+	}
+	
+	public void addEdge(Edge e, String diagramName){
+		if(diagramName == null)
+			currentEdges.add(e);
+		else
+			edges.get(diagramName).add(e);
+	}
+	
+	public Edge removeEdge(int edgeHashCode, String diagramName){
+		List<Edge> list = (diagramName == null) ? currentEdges : edges.get(diagramName);
+		for(Edge e : list){
+			if(e.diagramId == edgeHashCode){
+				list.remove(e);
+				return e;
+			}
+		}
+		return null;
+	}
+	
+	public Edge getEdge(int edgeHashCode, String diagramName){
+		List<Edge> list = (diagramName == null) ? currentEdges : edges.get(diagramName);
+		for(Edge e : list){
+			if(e.diagramId == edgeHashCode){
+				return e;
+			}
+		}
+		return null;
+	}
+	
+	public List<Node> getCurrentNodes(){
+		return currentNodes;
+	}
+	
+	public List<Edge> getCurrentEdges(){
+		return currentEdges;
+	}
+	
+	private Map<String,List<Node>> nodes;
+	private Map<String,List<Edge>> edges;
+	private List<Node> currentNodes;
+	private List<Edge> currentEdges;
+}
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListener.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListener.java	Tue Jul 10 22:39:37 2012 +0100
@@ -15,55 +15,13 @@
 
  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.haptics;
 
 /**
- *
- * An HapticListeners is a thread listening to commands sent by an haptic device through 
- * shared memory and executes them. The piece of software that manages the haptic device
- * runs on its own thread, hence the need for a inter-thread communication. Listening 
- * to the haptic device cannot be done by the event dispatching thread as this 
- * would prevent the user from using the graphical user interface, therefore a further thread is needed 
- * for this task.  
- * HapticListener is an abstract class which must be extended by implementing the 
- * {@link #executeCommand(HapticListenerCommand, int, double, double, double, double)} method.    
- *
+ * An interface for executing commands sent from an haptic device.  
  */
-public abstract class HapticListener extends Thread {
-
-	public HapticListener() {
-		super("Haptic Listener");
-		mustSayGoodBye = false;
-	}
-
-	@Override
-	public final void run(){
-		synchronized(this){
-			while(!mustSayGoodBye){
-				try {
-					wait();
-					executeCommand(HapticListenerCommand.fromChar(cmd), diagramElementID, x, y, startX, startY);
-					notify(); // notify the command has been executed 
-				} catch (InterruptedException e) {
-					dispose();
-				}
-			}
-		}
-	}
-	
-	public abstract void executeCommand(HapticListenerCommand cmd, int ID, double x, double y, double startX, double startY);
-	
-	public void dispose(){
-		mustSayGoodBye = true;
-	}
-	
-	private char cmd;
-	private int diagramElementID;
-	private boolean mustSayGoodBye; 
-	private double x;
-	private double y;
-	private double startX;
-	private double startY;
+public interface HapticListener {
+	public void executeCommand(HapticListenerCommand cmd, int ID, double x, double y, double startX, double startY);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread.java	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,72 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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.haptics;
+
+/**
+ *
+ * An HapticListenerThread is a thread listening to commands sent by an haptic device through 
+ * shared memory and executes them. The piece of software that manages the haptic device
+ * runs on its own thread, hence the need for a inter-thread communication. Listening 
+ * to the haptic device cannot be done by the event dispatching thread as this 
+ * would prevent the user from using the graphical user interface, therefore a further thread is needed 
+ * for this task.  
+ * HapticListener is an abstract class which must be extended by implementing the 
+ * {@link #executeCommand(HapticListenerCommand, int, double, double, double, double)} method.    
+ *
+ */
+public abstract class HapticListenerThread extends Thread implements HapticListener {
+
+	public HapticListenerThread() {
+		super("Haptic Listener");
+		mustSayGoodBye = false;
+	}
+
+	@Override
+	public final void run(){
+		synchronized(this){
+			while(!mustSayGoodBye){
+				try {
+					wait();
+					executeCommand(HapticListenerCommand.fromChar(cmd), diagramElementID, x, y, startX, startY);
+					notify(); // notify the command has been executed 
+				} catch (InterruptedException e) {
+					dispose();
+				}
+			}
+		}
+	}
+	
+	@Override
+	public abstract void executeCommand(HapticListenerCommand cmd, int ID, double x, double y, double startX, double startY);
+	
+	public abstract HapticListener getNonRunnableListener();
+		
+	public void dispose(){
+		mustSayGoodBye = true;
+	}
+	
+	private char cmd;
+	private int diagramElementID;
+	private boolean mustSayGoodBye; 
+	private double x;
+	private double y;
+	private double startX;
+	private double startY;
+}
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.dll has changed
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/Haptics.java	Tue Jul 10 22:39:37 2012 +0100
@@ -20,14 +20,16 @@
 package uk.ac.qmul.eecs.ccmi.haptics;
 
 import java.awt.geom.Line2D;
-import java.io.IOException;
 import java.util.BitSet;
 
+/**
+ * 
+ * An interface for rendering a visual diagram hapticly.   
+ *
+ */
 public interface Haptics extends Runnable{
 
-	public int init(int width, int height) throws IOException;
-
-	public void addNewDiagram(String diagramName, boolean switchAfter);
+	public void addNewDiagram(String diagramName);
 
 	public void switchDiagram(String diagramName);
 
@@ -53,6 +55,8 @@
 	public void pickUp(int elementHashCode);
 	
 	public boolean isAlive();
+	
+	public void setVisible(boolean visible);
 
 	public void dispose();
 	
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticsFactory.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/HapticsFactory.java	Tue Jul 10 22:39:37 2012 +0100
@@ -19,6 +19,8 @@
 
 package uk.ac.qmul.eecs.ccmi.haptics;
 
+import uk.ac.qmul.eecs.ccmi.utils.PreferencesService;
+
 /**
  *
  * Creates an instance of a class implementing the Haptics interface. There can only be one instance of such 
@@ -33,12 +35,37 @@
 	 *  
 	 * @param listener an haptic commands listener to link to the {@code Haptics} instance.
 	 */
-	public static void createInstance(HapticListener listener) {
+	public static void createInstance(HapticListenerThread listener) {
 		if(hapticsInstance != null)
 			throw new IllegalStateException("create instance must be called once only");
-		hapticsInstance = OmniHaptics.createInstance(listener);
+		
+		/* use the preferences service to pick the right device to use (the one the user selected *
+		 * in their preferences). This allows to avoid loading the .dll file for the other device *
+		 * that the user doesn't need, which would entail a waste of memory and, more important   *
+		 * a conflict between function names.                                                     */
+		String defaultDevice = PreferencesService.getInstance().get("haptic_device", TABLET_ID);
+		
+		if(PHANTOM_ID.equals(defaultDevice)){
+			/* OmniHaptics first */
+			hapticsInstance = OmniHaptics.createInstance(listener);
+			if(hapticsInstance != null)//OmniHaptics instance successfully created: return
+				return;
+		}else if(FALCON_ID.equals(defaultDevice)){ 
+			/* Falcon first  */
+			hapticsInstance = FalconHaptics.createInstance(listener);
+			if(hapticsInstance != null){ //FalconHaptics instance successfully created: return
+				return;
+			}
+		}
+		
+		/* no devices available, stop the listener and go for the default */
+		if(listener.isAlive()){
+			listener.interrupt();
+		}
+		hapticsInstance = MouseHaptics.createInstance(listener.getNonRunnableListener());
 		if(hapticsInstance == null)
-			hapticsInstance = new DummyHaptics();
+			throw new RuntimeException();
+		
 	}
 	
 	public static Haptics getInstance(){
@@ -49,4 +76,7 @@
 	}
 	
 	private static Haptics hapticsInstance;
+	public static final String PHANTOM_ID = "Phantom Omni";
+	public static final String FALCON_ID = "Falcon";
+	public static final String TABLET_ID = "Tablet/Mouse";
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/MouseHaptics.java	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,452 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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.haptics;
+
+import java.awt.AWTException;
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Robot;
+import java.awt.Stroke;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowFocusListener;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.io.IOException;
+import java.util.BitSet;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import uk.ac.qmul.eecs.ccmi.gui.DiagramPanel;
+import uk.ac.qmul.eecs.ccmi.main.DiagramEditorApp;
+import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
+
+@SuppressWarnings("serial")
+class MouseHaptics extends JPanel implements Haptics, KeyListener, MouseListener, MouseMotionListener {
+	static Haptics createInstance(HapticListener listener){
+		MouseHaptics instance = new MouseHaptics(listener);
+		try{
+			instance.initMouseHaptics();
+			return instance;
+		}catch(IOException ioe){
+			return null;
+		}
+	}
+	
+	private MouseHaptics(HapticListener listener){
+		this.listener = listener;
+		solidStroke = new BasicStroke(EDGE_THICKNESS);
+		dottedStroke = new BasicStroke(EDGE_THICKNESS,
+				BasicStroke.CAP_ROUND, 
+				BasicStroke.JOIN_ROUND, 
+				0.0f, 
+				new float[]{1.0f,30.0f}, 
+				0.0f);
+		dashedStroke = new BasicStroke(EDGE_THICKNESS,
+				BasicStroke.CAP_ROUND, 
+				BasicStroke.JOIN_ROUND, 
+				0.0f, 
+				new float[]{80.0f,50.0f}, 
+				0.0f);
+	}
+
+	@Override
+	public void paintComponent(Graphics g){
+		super.paintComponent(g);
+		
+		Graphics2D g2 = (Graphics2D)g;
+		
+		Stroke oldStroke = g2.getStroke();
+		// draw edges  
+		g2.setColor(EDGE_COLOR);
+		for(Edge e : listSupport.getCurrentEdges()){
+			switch(e.stipplePattern){
+			case Edge.SOLID_LINE :
+				g2.setStroke(solidStroke);break;
+			case Edge.DOTTED_LINE : 
+				g2.setStroke(dottedStroke);break;
+			case Edge.DASHED_LINE : 
+				g2.setStroke(dashedStroke);break;
+			}
+			for(int i=0; i< e.adjMatrix.length; i++){
+				 BitSet adj = e.adjMatrix[i];
+				 for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) {
+				     g2.drawLine((int)e.xs[i], (int)e.ys[i], (int)e.xs[j], (int)e.ys[j]);
+				 }
+			}
+		}
+		
+		g2.setStroke(oldStroke);
+		// draw nodes 
+		g2.setColor(NODE_COLOR);
+		for(Node n : listSupport.getCurrentNodes()){
+			g2.fillOval((int)(n.x - NODE_DIAMETER/2), (int)(n.y - NODE_DIAMETER/2), NODE_DIAMETER, NODE_DIAMETER);
+		}
+	}
+	
+	@Override
+	public void run() {}
+
+	void initMouseHaptics() throws IOException {
+		hapticFrame = new JFrame("Haptic Frame");
+		try {
+			robot = new Robot();
+		} catch (AWTException e) {
+			throw new IOException(e);
+		}
+		/* this is necessary to make the window screen size. it doens't work with the default layout */
+		setLayout(new BorderLayout());
+		
+		setBackground(Color.black);
+		
+		listSupport = new HapticListSupport();
+
+		hapticFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+		hapticFrame.addWindowFocusListener(new WindowFocusListener(){
+			@Override
+			public void windowGainedFocus(WindowEvent arg0) {
+				NarratorFactory.getInstance().speak("Haptic window Focused");
+			}
+
+			@Override
+			public void windowLostFocus(WindowEvent arg0) {}
+		});
+		hapticFrame.setContentPane(this);
+		hapticFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
+		hapticFrame.setUndecorated(true);
+		hapticFrame.addKeyListener(this);
+		addMouseMotionListener(this);
+		addMouseListener(this);
+		hapticFrame.pack();
+	}
+
+	@Override
+	public void addNewDiagram(String diagramName) {
+		listSupport.addNewDiagram(diagramName);
+		repaint();
+	}
+
+	@Override
+	public void switchDiagram(String diagramName) {
+		listSupport.switchDiagram(diagramName);
+		repaint();
+	}
+
+	@Override
+	public void removeDiagram(String diagramNameToRemove,
+			String diagramNameOfNext) {
+		listSupport.removeDiagram(diagramNameToRemove, diagramNameOfNext);
+		repaint();
+	}
+
+	@Override
+	public void addNode(double x, double y, int nodeHashCode, String diagramName) {
+		Node n = new Node(x,y,nodeHashCode, 0);
+		listSupport.addNode(n,diagramName);
+		repaint();
+	}
+
+	@Override
+	public void removeNode(int nodeHashCode, String diagramName) {
+		listSupport.removeNode(nodeHashCode, diagramName);
+		repaint();
+	}
+
+	@Override
+	public void moveNode(double x, double y, int nodeHashCode,
+			String diagramName) {
+		Node n = listSupport.getNode(nodeHashCode, diagramName);
+		n.x = x;
+		n.y = y;
+		repaint();
+	}
+
+	@Override
+	public void addEdge(int edgeHashCode, double[] xs, double[] ys,
+			BitSet[] adjMatrix, int nodeStart, int stipplePattern,
+			Line2D attractLine, String diagramName) {
+		// find the mid point of the line of attraction
+		double pX = Math.min(attractLine.getX1(), attractLine.getX2());
+		double pY = Math.min(attractLine.getY1(), attractLine.getY2());
+		pX += Math.abs(attractLine.getX1() -  attractLine.getX2())/2;
+		pY += Math.abs(attractLine.getY1() -  attractLine.getY2())/2;
+		Edge e = new Edge(edgeHashCode,0, xs, ys,
+				adjMatrix, nodeStart, stipplePattern,
+				pX, pY);
+		listSupport.addEdge(e, diagramName);
+		repaint();
+	}
+
+	@Override
+	public void updateEdge(int edgeHashCode, double[] xs, double[] ys,
+			BitSet[] adjMatrix, int nodeStart, Line2D attractLine,
+			String diagramName) {
+		Edge e = listSupport.getEdge(edgeHashCode, diagramName);
+		e.xs = xs;
+		e.ys = ys;
+		e.adjMatrix = adjMatrix;
+		e.nodeStart = nodeStart;
+		repaint();
+	}
+
+	@Override
+	public void removeEdge(int edgeHashCode, String diagramName) {
+		listSupport.removeEdge(edgeHashCode, diagramName);
+		repaint();
+	}
+
+	@Override
+	public void attractTo(int elementHashCode) {
+
+	}
+
+	@Override
+	public void pickUp(int elementHashCode) {
+		//	needed to change the status of the haptic device. not needed here
+	}
+
+	@Override
+	public boolean isAlive() {
+		return false;
+	}
+	
+	@Override
+	public void setVisible(boolean visible){
+		hapticFrame.setVisible(visible);
+	}
+
+	@Override
+	public void dispose() {
+		// no resources to free up
+	}
+	
+	@Override
+	public void keyPressed(KeyEvent evt) {
+		DiagramPanel panel = DiagramEditorApp.getFrame().getActiveTab();
+		if(panel == null)
+			DiagramEditorApp.getFrame().editorTabbedPane.dispatchEvent(evt);
+		else
+			panel.getTree().dispatchEvent(evt);
+	}
+
+	@Override
+	public void keyReleased(KeyEvent evt) {
+		keyPressed(evt);
+	}
+
+	@Override
+	public void keyTyped(KeyEvent evt) {
+		keyPressed(evt);
+	}
+	
+	@Override
+	public void mouseDragged(MouseEvent evt) {
+		/* priority to nodes: check if the mouse pointer is inside a circle centred on   *
+		 * the node and with radius equal to NODE_RADIUS. If the mouse pointer is within *
+		 * the radius of two or more nodes then the closest is picked up. The command is *
+		 * executed once when the node is touched. In order have it executed again the   *
+		 * used must get away from it and hover above it again                           */ 
+		Point2D p = evt.getPoint();
+		if(elementPickedUp){
+			draggedDistance += evt.getPoint().distance(lastDragPoint);  
+			if( draggedDistance > CHAIN_SOUND_INTERVAL){
+				listener.executeCommand(HapticListenerCommand.PLAY_SOUND, 3, 0,0,0,0);
+				draggedDistance = 0;
+			}
+			lastDragPoint = evt.getPoint();
+		}
+		
+		Node hoveredNode = null;
+		for(Node n : listSupport.getCurrentNodes()){
+			double distance = p.distance(n.x, n.y); 
+			if( distance < NODE_HOVER_DIST){
+				if(hoveredNode == null || distance < p.distance(hoveredNode.x,hoveredNode.y) ){
+					hoveredNode = n;
+				}
+			}
+		}
+		if(hoveredNode != null && hoveredNode != lastTouchedNode){
+			listener.executeCommand(HapticListenerCommand.PLAY_ELEMENT_SPEECH, hoveredNode.diagramId, 0, 0, 0, 0);
+			lastTouchedNode = hoveredNode;
+			lastTouchedEdge = null;
+			return; 
+		}
+		lastTouchedNode = hoveredNode;
+		/* if hovering inside a node neither send the command nor take edges into account */
+		if(hoveredNode != null)
+			return;
+		
+		/* if no node is being touched, check the edges out. */
+		Edge hoveredEdge = null;
+		Line2D line = new Line2D.Double();
+		/* look at all edges */
+		for(Edge e : listSupport.getCurrentEdges()){
+			/* look at all edge's lines */
+			for(int i=0; i< e.adjMatrix.length; i++){
+				 BitSet adj = e.adjMatrix[i];
+				 for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) {
+					 line.setLine(e.xs[i], e.ys[i], e.xs[j], e.ys[j]);
+					 if(lastTouchedEdge != e && line.ptSegDist(p)<EDGE_HOVER_DIST){
+						 hoveredEdge = e;
+						 listener.executeCommand(HapticListenerCommand.PLAY_ELEMENT_SPEECH, hoveredEdge.diagramId, 0, 0, 0, 0);
+						 lastTouchedEdge = hoveredEdge;
+						 return;
+					 }
+				 }
+			}
+		}
+		lastTouchedEdge = hoveredEdge;
+	}
+	
+	@Override
+	public void mouseMoved(MouseEvent evt) {
+		/* right click on a graphic tablet used as mouse has the effect of nullifying the           *
+		 * dragging. That is there is no right click but rather it's like if you untouch            *
+		 * the tablet. In order to address this a robot is used in order to re-leftclick each time  *
+		 * the right click is pressed. In this way we assure that the left click is always held     *
+		 * and therefore the mouse is always dragging rather than moving                            */
+		if(mustReclick){
+			mustReclick = false;
+			reclickedAfterMove = true; // this is to make this.mousePressed() have no effect, when it's the robot clicking 
+			robot.mousePress(InputEvent.BUTTON1_MASK);
+		}
+	}
+	
+	@Override
+	public void mousePressed(MouseEvent evt) {
+		/* by clicking on the object, its name is stated by the TTS.        *
+		 * Much as what happens when hovering on it with the button pressed */
+		if(evt.getButton() == MouseEvent.BUTTON1){
+			if(reclickedAfterMove){
+				reclickedAfterMove = false;
+				return;
+			}
+			lastTouchedEdge = null; // these two fields are used with dragging to avoid repeating 
+			lastTouchedNode = null;
+			
+			mouseDragged(evt);//left clicking on an object is like to drag on it 
+			return;
+		}
+		/* button 3 (right click) is for moving the objects (picking up and dropping) */
+		if(evt.getButton() != MouseEvent.BUTTON3)
+			return;
+		if(!secondClick){ // clicked for the first time: pick up the node or edge
+			Point2D p = evt.getPoint();
+			Node hoveredNode = null;
+			for(Node n : listSupport.getCurrentNodes()){
+				double distance = p.distance(n.x, n.y); 
+				if( distance < NODE_HOVER_DIST){
+					if(hoveredNode == null || distance < p.distance(hoveredNode.x,hoveredNode.y) ){
+						hoveredNode = n;
+					}
+				}
+			}
+			if(hoveredNode != null){ // clicked on a node 
+				listener.executeCommand(HapticListenerCommand.PICK_UP, hoveredNode.diagramId, 0, 0, 0, 0);
+				secondClick = true;
+				startX = evt.getX();
+				startY = evt.getY();
+				pickedUpElementId = hoveredNode.diagramId;
+				mustReclick = true;
+				/* sets the variables for the chain sound when dragging the element around */
+				elementPickedUp = true;
+				lastDragPoint = evt.getPoint();
+				return;
+			}
+			/* if no node is being touched, check the edges out. */
+			Line2D line = new Line2D.Double();
+			/* look at all edges */
+			for(Edge e : listSupport.getCurrentEdges()){
+				/* look at all edge's lines */
+				for(int i=0; i< e.adjMatrix.length; i++){
+					BitSet adj = e.adjMatrix[i];
+					for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) {
+						line.setLine(e.xs[i], e.ys[i], e.xs[j], e.ys[j]);
+						if(/*lastTouchedEdge != e && */line.ptSegDist(p)<EDGE_HOVER_DIST){
+							listener.executeCommand(HapticListenerCommand.PICK_UP, e.diagramId, 0, 0, 0, 0);
+							secondClick = true;
+							startX = evt.getX();
+							startY = evt.getY();
+							pickedUpElementId = e.diagramId;
+							mustReclick = true;
+							/* sets the variables for the chain sound when dragging the element around */
+							elementPickedUp = true;
+							lastDragPoint = evt.getPoint();
+							return;
+						}
+					}
+				}
+			}
+		}else{
+			listener.executeCommand(HapticListenerCommand.MOVE, pickedUpElementId, evt.getX(), evt.getY(), startX, startY);
+			elementPickedUp = false;
+			secondClick = false;
+		}
+		mustReclick = true;
+	}
+
+	@Override
+	public void mouseEntered(MouseEvent evt) {}
+
+	@Override
+	public void mouseExited(MouseEvent evt) {}
+
+	@Override
+	public void mouseClicked(MouseEvent evt) {}
+
+	@Override
+	public void mouseReleased(MouseEvent evt) {}
+	
+	private HapticListener listener;
+	private JFrame hapticFrame;
+	private HapticListSupport listSupport;
+	private Stroke solidStroke;
+	private Stroke dashedStroke;
+	private Stroke dottedStroke;
+	private Node lastTouchedNode;
+	private Edge lastTouchedEdge;
+	private Robot robot;
+	private boolean mustReclick;
+	private boolean reclickedAfterMove;
+	private boolean secondClick;
+	private boolean elementPickedUp;
+	private Point2D lastDragPoint;
+	private double draggedDistance;
+	private int startX;
+	private int startY;
+	private int pickedUpElementId;
+	private static final Color EDGE_COLOR = Color.RED;
+	private static final Color NODE_COLOR = Color.WHITE;
+	private static final int EDGE_THICKNESS = 26;//2;
+	private static final int NODE_DIAMETER = 50;//10;
+	private static final int NODE_HOVER_DIST = 25;
+	private static final int EDGE_HOVER_DIST = 13;
+	private static final double CHAIN_SOUND_INTERVAL = 150;
+	
+}
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/Node.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/Node.java	Tue Jul 10 22:39:37 2012 +0100
@@ -38,11 +38,7 @@
 	
 	public double x;
 	public double y;
-	/**
-	 * the id on the diagram "id space". it corresponds to the hash code of the 
-	 * diagram nodes
-	 * @see : uk.ac.eecs.qmul.ccmi.components.Node 
-	 */
+	/* the id on the diagram "id space". it corresponds to the hash code of the diagram nodes  */
 	public int diagramId; // not shared with the haptic thread
 	public int hapticId;
 	public ArrayList<Edge> edges;
Binary file java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.dll has changed
--- a/java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/haptics/OmniHaptics.java	Tue Jul 10 22:39:37 2012 +0100
@@ -41,23 +41,24 @@
  */
 class OmniHaptics extends Thread implements Haptics {
 	
-	static Haptics createInstance(HapticListener listener) {
+	static Haptics createInstance(HapticListenerThread listener) {
 		if(listener == null)
 			throw new IllegalArgumentException("listener cannot be null");
 		
 		if(OsDetector.isWindows()){
 			/* try to load .dll. First copy it in the home/ccmi_editor_data/lib directory  */
-			URL url = OmniHaptics.class.getResource("Haptics.dll");
+			URL url = OmniHaptics.class.getResource("OmniHaptics.dll");
 			ResourceFileWriter fileWriter = new ResourceFileWriter(url);
-			fileWriter.writeToDisk(
+			fileWriter.writeOnDisk(
 					PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir")),
-					"Haptics.dll");
+					"OmniHaptics.dll");
 			String path = fileWriter.getFilePath();
-			if(path == null)
-				return null;
 			try{
+				if(path == null)
+					throw new UnsatisfiedLinkError("OmniHaptics.dll missing");
 				System.load( path );
 			}catch(UnsatisfiedLinkError e){
+				e.printStackTrace();
 				return null; 
 			}
 		}else{
@@ -80,11 +81,12 @@
 			}
 		}
 		if(omniHaptics.hapticInitFailed){
-				/* the initialization has failed, the haptic thread is about to die */
-				while(!omniHaptics.hapticListener.isInterrupted()){
-					omniHaptics.hapticListener.interrupt();
-				}
-				omniHaptics.hapticListener = null; //leave the listener to the GC
+				/* the initialization has failed, the haptic thread is about to die         */
+				/* don't kill the listener as initialization will be tried on other devices */
+//				while(!omniHaptics.hapticListener.isInterrupted()){
+//					omniHaptics.hapticListener.interrupt();
+//				}
+//				omniHaptics.hapticListener = null; //leave the listener to the GC
 				return null;
 		}else{ 
 			return omniHaptics;
@@ -110,37 +112,34 @@
 		hapticInitFailed = false;
 		nodes = new HashMap<String,ArrayList<Node>>();
 		edges = new HashMap<String,ArrayList<Edge>>();
-		currentNodes = EMPTY_NODE_LIST;
-		currentEdges = EMPTY_EDGE_LIST;
+		currentNodes = Empties.EMPTY_NODE_LIST;
+		currentEdges = Empties.EMPTY_EDGE_LIST;
 		nodesMaps = new HashMap<String,HashMap<Integer,Node>>();
 		edgesMaps = new HashMap<String,HashMap<Integer,Edge>>();
-		currentNodesMap = EMPTY_NODE_MAP;
-		currentEdgesMap = EMPTY_EDGE_MAP;
+		currentNodesMap = Empties.EMPTY_NODE_MAP;
+		currentEdgesMap = Empties.EMPTY_EDGE_MAP;
 	}	
 	
 	
-	@Override
-	public native int init(int width, int height) throws IOException;
+	public native int initOmni(int width, int height) throws IOException;
 	
 	@Override
-	public void addNewDiagram(String name, boolean switchAfter){
+	public void addNewDiagram(String diagramName){
 		ArrayList<Node> cNodes = new ArrayList<Node>(30);
 		ArrayList<Edge> cEdges = new ArrayList<Edge>(30);
-		nodes.put(name, cNodes);
-		edges.put(name, cEdges);
+		nodes.put(diagramName, cNodes);
+		edges.put(diagramName, cEdges);
 		
 		HashMap<Integer,Node> cNodesMap = new HashMap<Integer,Node>();
 		HashMap<Integer,Edge> cEdgesMap = new HashMap<Integer,Edge>();
-		nodesMaps.put(name, cNodesMap);
-		edgesMaps.put(name, cEdgesMap);
+		nodesMaps.put(diagramName, cNodesMap);
+		edgesMaps.put(diagramName, cEdgesMap);
 		
-		if(switchAfter){
-			synchronized(this){
-				currentNodes = cNodes;
-				currentEdges = cEdges;
-				currentNodesMap = cNodesMap;
-				currentEdgesMap = cEdgesMap;
-			}
+		synchronized(this){
+			currentNodes = cNodes;
+			currentEdges = cEdges;
+			currentNodesMap = cNodesMap;
+			currentEdgesMap = cEdgesMap;
 		}
 	}
 	
@@ -148,7 +147,7 @@
 	public synchronized void switchDiagram(String diagramName){
 		// check nodes only, as the edges and nodes maps are strongly coupled
 		if(!nodes.containsKey(diagramName))
-			throw new IllegalArgumentException("Diagram " + diagramName + " not present among the current ones");
+			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramName);
 		
 		currentNodes = nodes.get(diagramName);
 		currentEdges = edges.get(diagramName);
@@ -159,20 +158,20 @@
 	@Override
 	public synchronized void removeDiagram(String diagramNameToRemove, String diagramNameNext){
 		if(!nodes.containsKey(diagramNameToRemove))
-			throw new IllegalArgumentException("Id " + diagramNameToRemove + " not present aong the current ones");
+			throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameToRemove);
 		
 		nodes.remove(diagramNameToRemove);
 		edges.remove(diagramNameToRemove);
 		nodesMaps.remove(diagramNameToRemove);
 		edgesMaps.remove(diagramNameToRemove);
 		if(diagramNameNext == null){
-			currentNodes = EMPTY_NODE_LIST;
-			currentEdges = EMPTY_EDGE_LIST;
-			currentNodesMap = EMPTY_NODE_MAP;
-			currentEdgesMap = EMPTY_EDGE_MAP;
+			currentNodes = Empties.EMPTY_NODE_LIST;
+			currentEdges = Empties.EMPTY_EDGE_LIST;
+			currentNodesMap = Empties.EMPTY_NODE_MAP;
+			currentEdgesMap = Empties.EMPTY_EDGE_MAP;
 		}else{
 			if(!nodes.containsKey(diagramNameNext))
-					throw new IllegalArgumentException("Id " + diagramNameNext + " not present aong the current ones");
+					throw new IllegalArgumentException("Diagram not found among added diagrams:" + diagramNameNext);
 			currentNodes = nodes.get(diagramNameNext);
 			currentEdges = edges.get(diagramNameNext);
 			currentNodesMap = nodesMaps.get(diagramNameNext);
@@ -239,7 +238,7 @@
 		for(Node n : iterationList){
 			if(n.diagramId == nodeHashCode){
 				n.x = x;
-				n.y = y;
+				n.y = y; 
 				break;
 			}
 		}
@@ -266,12 +265,12 @@
 		if(diagramName == null){
 			/* add the edge reference to the Haptic edges list */
 			currentEdges.add(e);
-			/* add the edge reference to the haptic edges map */
+			/* add the edge reference to the Haptic edges map */
 			currentEdgesMap.put(currentHapticId, e);
 		}else{
 			/* add the edge reference to the Haptic edges list */
 			edges.get(diagramName).add(e);
-			/* add the edge reference to the haptic edges map */
+			/* add the edge reference to the Haptic edges map */
 			edgesMaps.get(diagramName).put(currentHapticId, e);
 		}
 	}
@@ -366,6 +365,11 @@
 	}
 	
 	@Override
+	public void setVisible(boolean visible){
+		// not implemented but required by Haptic interface 
+	}
+	
+	@Override
 	public synchronized void dispose(){
 		shutdown = true;
 		/* wait for the haptic thread to shut down */
@@ -379,7 +383,7 @@
 	@Override
 	public void run() {
 		try {
-			init(width,height);
+			initOmni(width,height);
 		} catch (IOException e) {
 			throw new RuntimeException();// OMNI haptic device doesn't cause any exception
 		}
@@ -421,11 +425,6 @@
 	private boolean pickUp;
 	/* currentHapticId is used to share haptic ids between the threads */
 	private int currentHapticId;
-	private HapticListener hapticListener;
-	
-	private static final ArrayList<Node> EMPTY_NODE_LIST = new ArrayList<Node>(0);
-	private static final ArrayList<Edge> EMPTY_EDGE_LIST = new ArrayList<Edge>(0);
-	private static final HashMap<Integer,Node> EMPTY_NODE_MAP = new HashMap<Integer,Node>();
-	private static final HashMap<Integer,Edge> EMPTY_EDGE_MAP = new HashMap<Integer,Edge>();
+	private HapticListenerThread hapticListener;
 
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.java	Tue Jul 10 22:39:37 2012 +0100
@@ -26,7 +26,9 @@
 import java.text.MessageFormat;
 import java.util.ResourceBundle;
 
+import javax.swing.JOptionPane;
 import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
 
 import uk.ac.qmul.eecs.ccmi.gui.EditorFrame;
 import uk.ac.qmul.eecs.ccmi.gui.HapticKindle;
@@ -50,29 +52,10 @@
  */
 public class DiagramEditorApp implements Runnable {
 	
-	/**
-	 *  perform initialization prior to displaying the GUI 
-	 * @throws IOException 
-	 */
-	public void init(String[] args) throws IOException {
+	/* initialize all the non gui resources */
+	private void init() throws IOException {
 		Thread.setDefaultUncaughtExceptionHandler(new CCmIUncaughtExceptionHandler());
-		final ResourceBundle resources = ResourceBundle.getBundle(this.getClass().getName());
-		/* read command line arguments */
-		if(args.length > 1){
-			System.out.println(resources.getString("usage"));
-			System.exit(-1);
-		}
-		boolean enableLog = false;
-		if(args.length == 1){
-			if(args[0].equals("-l")){
-				enableLog = true;
-				System.out.println("log enabled");
-			}else{
-				System.out.println(resources.getString("usage"));
-				System.exit(-1);
-			}
-		}
-		
+		final ResourceBundle resources = ResourceBundle.getBundle(DiagramEditorApp.class.getName());
 		/* create the home directory if it does not exist and store the path into the preferences */
 		PreferencesService preferences = PreferencesService.getInstance(); 
 		String homeDirPath = preferences.get("home", null);
@@ -93,26 +76,26 @@
 		
 		/* create the images directory into the home directory */
 		File imagesDir = new File(homeDir,resources.getString("dir.images"));
-		if(mkDir(imagesDir,resources))
-			preferences.put("dir.images", imagesDir.getAbsolutePath());
+		mkDir(imagesDir,resources);
+		preferences.put("dir.images", imagesDir.getAbsolutePath());
 		
 		/* create the diagrams dir into the home directory */
 		File diagramDir = new File(homeDir,resources.getString("dir.diagrams"));
-		if(mkDir(diagramDir,resources))
-			preferences.put("dir.diagrams", diagramDir.getAbsolutePath());
+		mkDir(diagramDir,resources);
+		preferences.put("dir.diagrams", diagramDir.getAbsolutePath());
 		
 		/* create the libs directory into he home directory */
 		File libsDir = new File(homeDir,resources.getString("dir.libs"));
-		if(mkDir(libsDir,resources))
-			preferences.put("dir.libs", libsDir.getAbsolutePath());
+		mkDir(libsDir,resources);
+		preferences.put("dir.libs", libsDir.getAbsolutePath());
 		
 		/* write the template files included in the software in the template dir, if they don't exist yet */
 		ResourceFileWriter resourceWriter = new ResourceFileWriter(getClass().getResource("UML Diagram.xml"));
-		resourceWriter.writeToDisk(templateDir.getAbsolutePath(),"UML Diagram.xml");
-		resourceWriter.serResource(getClass().getResource("Tube.xml"));
-		resourceWriter.writeToDisk(templateDir.getAbsolutePath(),"Tube.xml");
-		resourceWriter.serResource(getClass().getResource("Organization Chart.xml"));
-		resourceWriter.writeToDisk(templateDir.getAbsolutePath(),"Organization Chart.xml");
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"UML Diagram.xml");
+		resourceWriter.setResource(getClass().getResource("Tube.xml"));
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"Tube.xml");
+		resourceWriter.setResource(getClass().getResource("Organization Chart.xml"));
+		resourceWriter.writeOnDisk(templateDir.getAbsolutePath(),"Organization Chart.xml");
 		
 		/* read the template files into an array to pass to the EditorFrame instance */
 		FilenameFilter filter = new FilenameFilter() {
@@ -122,30 +105,70 @@
 		    }
 		};
 		templateFiles = templateDir.listFiles(filter);
-
-		if(enableLog){
-			File logDir = new File(homeDir,resources.getString("dir.log"));
-			mkDir(logDir,resources);
+		File logDir = new File(homeDir,resources.getString("dir.log"));
+		mkDir(logDir,resources);
+		preferences.put("dir.log", logDir.getAbsolutePath());
+		
+		String enableLog = preferences.get("enable_log", "false");
+		if(Boolean.parseBoolean(enableLog)){
 			try{
 				InteractionLog.enable(logDir.getAbsolutePath());
 				InteractionLog.log("PROGRAM STARTED");
 			}catch(IOException ioe){
 				/* if logging was enabled, the possibility to log is considered inescapable */
-				/* do not allow the execution to continue any further                       */
+				/* at launch time: do not allow the execution to continue any further       */
 				throw new RuntimeException(ioe);
 			}
 		}
 		
-		/* create sound, speech and haptic engines */
+		/* create sound, speech engines */
 		NarratorFactory.createInstance();
 		SoundFactory.createInstance();
-		
+	}
+	
+	/* loads haptic device. If the user is running the software for the first time *
+	 * they're prompted with a dialog to select the device they want to use        */
+	private void initHaptics(){
+		final PreferencesService preferences = PreferencesService.getInstance();
+		if(!Boolean.parseBoolean(preferences.get("haptic_device.initialized", "false"))){
+			try {
+				SwingUtilities.invokeAndWait(new Runnable(){
+					@Override
+					public void run() {
+						try {
+							UIManager.setLookAndFeel(
+									UIManager.getSystemLookAndFeelClassName());
+						}catch(Exception e){/* nevermind */}
+						
+						String [] hapticDevices = {
+								HapticsFactory.PHANTOM_ID,
+								HapticsFactory.FALCON_ID,
+								HapticsFactory.TABLET_ID};
+						String selection = (String)SpeechOptionPane.showSelectionDialog(null, 
+								ResourceBundle.getBundle(DiagramEditorApp.class.getName()).getString("haptics_init.welcome"), 
+								hapticDevices, 
+								hapticDevices[0]);
+						if(selection == null)
+							System.exit(0);
+						preferences.put("haptic_device", selection);
+						preferences.put("haptic_device.initialized", "true");
+					}
+				});
+			}catch(InvocationTargetException ite){
+				throw new RuntimeException(ite);
+			}catch(InterruptedException ie){
+				throw new RuntimeException(ie);
+			}
+		}
+	
+		/* creates the Haptics instance */
 		HapticsFactory.createInstance(new HapticKindle());
 		haptics = HapticsFactory.getInstance();
 		if(haptics.isAlive())
 			NarratorFactory.getInstance().speakWholeText("Haptic device successfully initialized");
 	}
-	
+
+	/* return true if the directory was created, false if it existed before */
 	private boolean mkDir(File dir,ResourceBundle resources) throws IOException{
 		boolean created = dir.mkdir();
 		if(!dir.exists())
@@ -162,8 +185,6 @@
 	@Override
 	public void run() {
 		editorFrame = new EditorFrame(haptics,templateFiles,backupDirPath,getTemplateEditors());
-//		if(hapticKindle != null)
-//			hapticKindle.setEditorFrame(editorFrame);
 	}
 	
 	public TemplateEditor[] getTemplateEditors(){
@@ -173,32 +194,39 @@
 	}
 
 	/**
-	 * @param args
+	 * The main function 
+	 * @param args this software accepts no args from the command line
 	 */
-	public static void main(String[] args) {
+	public static void main(String[] args) {	
 		DiagramEditorApp application = new DiagramEditorApp();
-		
-		try {
-			application.init(args);
+		try{
+			application.init();
 		} catch (IOException e) {
 			final String msg = e.getLocalizedMessage();
 			try {
 				SwingUtilities.invokeAndWait(new Runnable(){
 					@Override
 					public void run(){
-						SpeechOptionPane.showMessageDialog(null, msg);
+						try {
+							UIManager.setLookAndFeel(
+									UIManager.getSystemLookAndFeelClassName());
+						}catch(Exception e){/* nevermind */}
+						JOptionPane.showMessageDialog(null, msg);
 					}
 				});
-				System.exit(-1);
 			} catch (InterruptedException ex) {
 				throw new RuntimeException(ex);
 			} catch (InvocationTargetException ex) {
 				throw new RuntimeException(ex);
 			}
+			System.exit(-1);
 		}
 		
+		application.initHaptics();
+		
+		/* start the application */
 		try {
-            SwingUtilities.invokeAndWait(application);
+			SwingUtilities.invokeAndWait(application);
         } catch (InvocationTargetException ex) {
             throw new RuntimeException(ex);
         } catch (InterruptedException ex) {
@@ -206,6 +234,12 @@
         }    
 	}
 	
+	/**
+	 * Returns the reference to the unique {@code EditorFrame} instance of the program. 
+	 * The main GUI class. 
+	 *    
+	 * @return an reference to {@code EditorFrame} 
+	 */
 	public static EditorFrame getFrame(){
 		return editorFrame;
 	}
--- a/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.properties	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/main/DiagramEditorApp.properties	Tue Jul 10 22:39:37 2012 +0100
@@ -7,11 +7,11 @@
 dir.images=images
 dir.libs=libs
 dir.log=log
-usage=Unrecognized option(s)\nUsage : java -jar ccmi.jar [-l] \n  -l : enables interaction log
 
 dir.error_msg=Could not create the following directory: {0}\u000A\
 Please check directory permissions and try again.
 
+haptics_init.welcome=Select the haptic device you want to use 
 
 #### APPLICATION PREFERENCES ####
 # server.local_port
@@ -20,6 +20,7 @@
 # dir.diagrams
 # dir.images
 # dir.libs
+# dir.log
 # laf 
 # recent
 # use_accessible_filechooser
@@ -28,3 +29,6 @@
 # server.local_port
 # server.remote_port
 # second_voice_enabled
+# haptic_device
+# haptic_device.initialized
+# enable_log
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/Command.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/Command.java	Tue Jul 10 22:39:37 2012 +0100
@@ -78,7 +78,7 @@
 	/**
 	 * Utility method to log, through the interaction log, the receipt of a command 
 	 * @param cmd the received command 
-	 * @param action
+	 * @param action a further description of the action that triggered this command 
 	 */
 	public static void log(Command cmd, String action){
 		if(cmd.getName() != Command.Name.LOCAL && cmd.getName() != Command.Name.BEND 
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/LockMessage.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/LockMessage.java	Tue Jul 10 22:39:37 2012 +0100
@@ -35,6 +35,12 @@
 
 	/**
 	 * Creates a lock message for the given diagram and with the given timestamp.
+	 * 
+	 * @param name the message name  
+	 * @param timestamp the time when the message was issued 
+	 * @param diagram the name of the diagram this message refers to 
+	 * @param args arguments of the message 
+	 * @param source the source that generated this message see {@link DiagramEventActionSource}
 	 */
 	public LockMessage(Name name, long timestamp, String diagram, Object[] args, Object source) {
 		super(timestamp, diagram,source);
@@ -45,6 +51,11 @@
 	/**
 	 * Creates a lock message for the given diagram and timestamp of the moment 
 	 * the message is created.
+	 * 
+	 * @param name the message name  
+	 * @param diagram the name of the diagram this message refers to 
+	 * @param args arguments of the message 
+	 * @param source the source that generated this message see {@link DiagramEventActionSource}
 	 */
 	public LockMessage(Name name, String diagram, Object[] args, DiagramEventActionSource source) {
 		super(diagram,source);
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/NetDiagram.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/NetDiagram.java	Tue Jul 10 22:39:37 2012 +0100
@@ -423,7 +423,6 @@
 		 * This class wraps an existing diagram into a network diagram. 
 		 * The network diagrams returns a TreeModelNetWrap and a CollectionModelNetWrap
 		 * when the relative getters are called
-		 * @see TreeModelNetWrap, CollectionModelNetWrap
 		 * 
 		 * @param diagram the diagram to wrap
 		 * @param connectionManager a connected socket channel
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/ProtocolFactory.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/ProtocolFactory.java	Tue Jul 10 22:39:37 2012 +0100
@@ -19,6 +19,9 @@
 
 package uk.ac.qmul.eecs.ccmi.network;
 
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * The factory class to create {@code Protocol} instances. 
  *
@@ -27,4 +30,16 @@
     public static Protocol newInstance(){
     	return new OscProtocol();
 	}
+    
+    /**
+	 * Utility method to check if an address is in a valid IPv4 format.
+	 * @param addr the address to check 
+	 * @return {@code true} if {@code addr} is in a valid IPv4 format 
+	 */
+	public static boolean validateIPAddr(String addr){
+		Matcher m = ip.matcher(addr);
+		return m.matches();
+	}
+	
+	private static final Pattern ip = Pattern.compile("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b");
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/Server.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/Server.java	Tue Jul 10 22:39:37 2012 +0100
@@ -61,7 +61,7 @@
 	 * Create a new server instance. If another instance is already running then it's shut
 	 * down before the new instance is created. 
 	 * 
-	 * @return a {@Server} instance.
+	 * @return a {@code Server} instance.
 	 */
 	public static Server createServer(){
 		if(server != null)
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/ServerConnectionManager.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/ServerConnectionManager.java	Tue Jul 10 22:39:37 2012 +0100
@@ -422,7 +422,8 @@
 				if(channel != localChannel){
 					/* update the local awareness panel and speech */ 
 					awarenessPanelEditor.addTimedRecord(awMsg.getDiagram(), DisplayFilter.getInstance().processForText(processedSource));
-					NarratorFactory.getInstance().speakWholeText(DisplayFilter.getInstance().processForSpeech(processedSource), Narrator.SECOND_VOICE);
+					NarratorFactory.getInstance().speakWholeText(
+							DisplayFilter.getInstance().processForSpeech(processedSource), Narrator.SECOND_VOICE);
 				}
 				/* broadcast the awareness message to all the clients but one which sent the    *
 				 * command and the local one to inform them the action has started              */
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/ServerLockManager.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/ServerLockManager.java	Tue Jul 10 22:39:37 2012 +0100
@@ -209,13 +209,13 @@
 	/**
 	 * Request an editing lock for a tree node 
 	 * 
-	 * @param treeNode : the treeNode the caller is trying to lock
-	 * @param lock : the type of lock requested 
-	 * @param channel : the channel works as a unique identifier for the clients
+	 * @param treeNode the treeNode the caller is trying to lock
+	 * @param lock the type of lock requested 
+	 * @param channel the channel works as a unique identifier for the clients
+	 * @param diagramName the name of the diagram the lock is requested on
 	 * @return true if the lock is successfully granted, or false otherwise (because of another client 
 	 *   holding a lock which clashes with this request) 
 	 */
-	public static ServerLockManager singletonLockManager;
 	public boolean requestLock(DiagramTreeNode treeNode, Lock lock, SocketChannel channel,String diagramName){
 //		System.out.println("lock before request:"+lockStatusDescription(diagramName)+"\n----");
 		List<LockEntry> locks = locksMap.get(diagramName);
@@ -224,7 +224,6 @@
 			/*  there is no entry in the map and one must be created */
 			locks = new LinkedList<LockEntry>();
 			locksMap.put(diagramName,locks);
-			singletonLockManager = this;
 		}
 		/* deleting a node will cause all the attached two-ended edges to    * 
 		 * be deleted, therefore we need to lock all those edges too, before */
--- a/java/src/uk/ac/qmul/eecs/ccmi/network/ServerNotRunningException.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/network/ServerNotRunningException.java	Tue Jul 10 22:39:37 2012 +0100
@@ -21,7 +21,6 @@
 
 /**
  * Exception thrown when the user tries to share a diagram before starting the server
- *
  */
 @SuppressWarnings("serial")
 public class ServerNotRunningException extends DiagramShareException {
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/EdgeDrawSupport.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/EdgeDrawSupport.java	Tue Jul 10 22:39:37 2012 +0100
@@ -28,6 +28,7 @@
 
 import javax.swing.JLabel;
 
+import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramTreeNode;
 import uk.ac.qmul.eecs.ccmi.gui.GraphPanel;
 
 
@@ -37,16 +38,17 @@
  */
 public abstract class EdgeDrawSupport {
 	/**
-	Draws a string.
-	   @param g2 the graphics context
-	   @param p an endpoint of the segment along which to
-	   draw the string
-	   @param q the other endpoint of the segment along which to
-	   draw the string
-	   @param s the string to draw
-	   @param center true if the string should be centered
-	   along the segment 
-	 * 
+	 *  Draws a string in proximity of a edge.
+	 *  @param g2 the graphics context
+	 *  @param p an endpoint of the segment along which to
+	 *  draw the string
+	 *  @param q the other endpoint of the segment along which to
+	 *  draw the string
+	 *  @param arrow the arrow head painted on the edge end where the string 
+	 *   is to be painted  
+	 *  @param s the string to draw
+	 *  @param center true if the string should be centered
+	 *  along the segment 
 	 */
 
 	public static void drawString(Graphics2D g2, 
@@ -69,6 +71,14 @@
 		g2.translate(-b.getX(), -b.getY());        
 	}
 
+	/**
+	 * Draws a graphical marker when the edge has notes associated to it 
+	 * @see uk.ac.qmul.eecs.ccmi.diagrammodel.TreeModel#setNotes(DiagramTreeNode, String, Object)  
+	 * 
+	 * @param g2 the graphics context
+	 * @param p an endpoint of the segment along which to draw the string
+	 *  @param q the other endpoint of the segment along which to draw the string
+	 */
 	public static void drawMarker(Graphics2D g2, Point2D p, Point2D q){
 		Point2D attach = q;
 		if (p.getX() > q.getX()){ 
@@ -83,17 +93,9 @@
 		g2.setColor(oldColor);
 	}
 
-	/**
+	/*
 	   Computes the attachment point for drawing a string.
-	   @param g2 the graphics context
-	   @param p an endpoint of the segment along which to
-	   draw the string
-	   @param q the other endpoint of the segment along which to
-	   draw the string
-	   @param b the bounds of the string to draw
-	   @param center true if the string should be centered
-	   along the segment
-	   @return the point at which to draw the string
+	   return the point at which to draw the string
 	 */
 	private static Point2D getAttachmentPoint(Graphics2D g2, 
 			Point2D p, Point2D q, ArrowHead arrow, Dimension d, boolean center){    
@@ -135,17 +137,9 @@
 		return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff);
 	}
 
-	/**
-		   Computes the extent of a string that is drawn along a line segment.
-		   @param g2 the graphics context
-		   @param p an endpoint of the segment along which to
-		   draw the string
-		   @param q the other endpoint of the segment along which to
-		   draw the string
-		   @param s the string to draw
-		   @param center true if the string should be centered
-		   along the segment
-		   @return the rectangle enclosing the string
+	/*
+	 * Computes the extent of a string that is drawn along a line segment.
+	 * The rectangle enclosing the string
 	 */
 	private static Rectangle2D getStringBounds(Graphics2D g2, 
 			Point2D p, Point2D q, ArrowHead arrow, String s, boolean center){
@@ -158,6 +152,7 @@
 		return new Rectangle2D.Double(a.getX(), a.getY(), d.getWidth(), d.getHeight());
 	}
 
+	/* size of the marker when an edge as notes */
 	private static final int MARKER_SIZE = 7;
 	private static JLabel label = new JLabel();
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/ModifierView.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/ModifierView.java	Tue Jul 10 22:39:37 2012 +0100
@@ -21,9 +21,9 @@
 
 /**
  * This immutable class represents the view associated with the modifier type
- * associated with it in a {@link NodeProperties} instance. 
+ * associated with it in a {@code NodeProperties} instance. 
  * 
- * */
+ */
 public class ModifierView {
 	
 	public ModifierView(boolean underline, boolean bold, boolean italic,
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/MultiLineString.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/MultiLineString.java	Tue Jul 10 22:39:37 2012 +0100
@@ -220,7 +220,6 @@
    
     /**
       Gets the bounding rectangle for this multiline string.
-      @param g2 the graphics context
       @return the bounding rectangle (with top left corner (0,0))
    */
    public Rectangle2D getBounds(){
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/PropertyView.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/PropertyView.java	Tue Jul 10 22:39:37 2012 +0100
@@ -24,7 +24,7 @@
 
 /**
  * This immutable class represents the view associated with the property type 
- * associated with it in a {@link NodeProperties} instance. 
+ * associated with it in a {@code NodeProperties} instance. 
  * 
  * */
 public class PropertyView {
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeEdge.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeEdge.java	Tue Jul 10 22:39:37 2012 +0100
@@ -43,6 +43,20 @@
 import uk.ac.qmul.eecs.ccmi.gui.persistence.PersistenceManager;
 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
 
+/**
+ * An edge rendered as a straight, dotted or dashed line. The edge can have an arrow head 
+ * at each end. Possible arrow heads are : 
+ * <ul>
+ * <li> Triangle
+ * <li> Black Triangle
+ * <li> V
+ * <li> Half V
+ * <li> Diamond
+ * <li> Black Diamond
+ * <li> Tail
+ * </ul> 
+ *
+ */
 @SuppressWarnings("serial")
 public class SimpleShapeEdge extends Edge {
 
@@ -53,16 +67,17 @@
 	}
 	
 	@Override
-	public boolean removeNode(DiagramNode n,Object source){
+	public boolean removeNode(DiagramNode n, Object source){
 		currentHeads.remove(n);
 		return super.removeNode(n,source);
 	}
 
+	
 	@Override
 	public void draw(Graphics2D g2) {
 		 Stroke oldStroke = g2.getStroke();
 		 g2.setStroke(getStyle().getStroke());
-		
+		 
 		 /* straight line  */
 		 if(points.isEmpty()){
 			 Line2D line = getSegment(getNodeAt(0),getNodeAt(1));
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeNode.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SimpleShapeNode.java	Tue Jul 10 22:39:37 2012 +0100
@@ -98,8 +98,8 @@
 			 * node centre from its original position. the next line is to place it back to the right position   */
 			Point2D start = new Point2D.Double(boundsAfterReshape.getCenterX(),boundsAfterReshape.getCenterY());
 			translateImplementation(start,
-					boundsBeforeReshape.getCenterX() - boundsAfterReshape.getCenterX(),
-					boundsBeforeReshape.getCenterY() - boundsAfterReshape.getCenterY());
+				boundsBeforeReshape.getCenterX() - boundsAfterReshape.getCenterX(),
+				boundsBeforeReshape.getCenterY() - boundsAfterReshape.getCenterY());
 		}
 		super.notifyChange(evt);
 	}
@@ -116,9 +116,9 @@
 		 * point is at the same position as before just to keep it more consistent               */
 		Rectangle2D boundsAfterReshape = getBounds();
 		translateImplementation(
-				new Point2D.Double(),
-				boundsBeforeReshape.getX() - boundsAfterReshape.getX(),
-				boundsBeforeReshape.getY() - boundsAfterReshape.getY()
+			new Point2D.Double(),
+			boundsBeforeReshape.getX() - boundsAfterReshape.getX(),
+			boundsBeforeReshape.getY() - boundsAfterReshape.getY()
 		);
 	}
 	
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SpeechWizardDialog.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SpeechWizardDialog.java	Tue Jul 10 22:39:37 2012 +0100
@@ -82,7 +82,7 @@
 	
 	/**
 	 * Enables or disables the finish button.   
-	 * @param enabled
+	 * @param enabled {@code true} to enable the button, {@code false} to disable 
 	 */
 	public void setFinishButtonEnabled(boolean enabled){
 		finished = enabled;
--- a/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/Wizard.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/Wizard.java	Tue Jul 10 22:39:37 2012 +0100
@@ -73,7 +73,7 @@
  * how to build a template diagram. A template diagram is a prototype diagram
  * (containing prototype nodes and edges) which can later on be used for creating instances
  * of that type of diagram through clonation. The wizard is completely accessible via audio 
- * as all the content and all focused components names are spoken out by the {@link Narrator} through a text to speech synthesizer.   
+ * as all the content and all focused components names are spoken out by the {@code Narrator} through a text to speech synthesizer.   
  *
  */
 public class Wizard {
--- a/java/src/uk/ac/qmul/eecs/ccmi/speech/BeadsAudioPlayer.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/speech/BeadsAudioPlayer.java	Tue Jul 10 22:39:37 2012 +0100
@@ -39,6 +39,10 @@
 
 import com.sun.speech.freetts.audio.AudioPlayer;
 
+/**
+ * An implementation of {@code AudioPlayer} using the {@code Beads} 
+ * library.
+ */
 public class BeadsAudioPlayer implements AudioPlayer {
 	public BeadsAudioPlayer(){
 		format = new AudioFormat(8000f, 16, 1, true, true);
--- a/java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/speech/NativeNarrator.java	Tue Jul 10 22:39:37 2012 +0100
@@ -40,7 +40,7 @@
 			URL url = NativeNarrator.class.getResource("WinNarrator.dll");
 			if(url != null){
 				ResourceFileWriter fileWriter = new ResourceFileWriter(url);
-				fileWriter.writeToDisk(
+				fileWriter.writeOnDisk(
 					PreferencesService.getInstance().get("dir.libs", System.getProperty("java.io.tmpdir")),	
 					"CCmIWinNarrator.dll");
 				String path = fileWriter.getFilePath();
@@ -49,6 +49,7 @@
 						System.load( path );
 						nativeLibraryNotFound = false;
 					}catch(UnsatisfiedLinkError e){
+						e.printStackTrace();
 						/* do nothing: nativeLibraryNotFound won't be set to false */
 						/* which will trigger a NarratorException                  */ 
 					}
--- a/java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.java	Tue Jul 10 22:39:37 2012 +0100
@@ -25,9 +25,6 @@
 import java.awt.FocusTraversalPolicy;
 import java.awt.KeyboardFocusManager;
 import java.awt.event.ActionEvent;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
 import java.awt.event.KeyAdapter;
@@ -153,8 +150,9 @@
 	
 	/**
 	 * Returns a {@code speechKeyListener} using first voice (default) 
-	 * @param editableComponent
-	 * @return 
+	 * @param editableComponent whether this key listener is for a component 
+	 * that will be editable  
+	 * @return a key listener that utters the letters when typed 
 	 */
 	public static KeyListener getSpeechKeyListener(boolean editableComponent){
 		return getSpeechKeyListener(editableComponent,false);
@@ -168,10 +166,6 @@
 		return checkBoxItemListener;
 	}
 	
-	public static FocusListener getFocusSpeechListener(){
-		return focusListener;
-	}
-	
 	public static Action getShutUpAction(){
 		return shutUpAction;
 	}
@@ -393,12 +387,6 @@
 		}
 	};
 	
-	private static final FocusListener focusListener = new FocusAdapter(){
-		public void focusGained(FocusEvent evt){
-			NarratorFactory.getInstance().speak(getComponentSpeech(evt.getComponent()));
-		}
-	};
-	
 	@SuppressWarnings("serial")
 	private static final Action shutUpAction = new AbstractAction(){
 		@Override
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/CharEscaper.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/CharEscaper.java	Tue Jul 10 22:39:37 2012 +0100
@@ -29,8 +29,8 @@
 	 * The original {@code String} can be restored by passing the returned {@code String} to 
 	 * {@link #restoreNewline(String)}. Existing '|' characters will be escaped in order not to miss
 	 * them in the restore process.  
-	 * @param s the {@String} to remove new line characters from
-	 * @return a {@String} where all new line characters have been replaced by '|'
+	 * @param s the {@code String} to remove new line characters from
+	 * @return a {@code String} where all new line characters have been replaced by '|'
 	 */
 	public 	static String replaceNewline(String s){
 		String result = s.replace("|", "'|");
@@ -40,8 +40,8 @@
 	/**
 	 * Restores a {@code String} whose new line characters have been previously replaced by 
 	 * {@link #replaceNewline(String)}, to the original form.  
-	 * @param s
-	 * @return
+	 * @param s the string to be restored
+	 * @return the restored string
 	 */
 	public static String restoreNewline(String s){
 		String result = s.replaceAll("([^'])\\|","$1\n");
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/ExceptionHandler.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/ExceptionHandler.java	Tue Jul 10 22:39:37 2012 +0100
@@ -19,6 +19,16 @@
 
 package uk.ac.qmul.eecs.ccmi.utils;
 
+/**
+ * An interface implemented by classes that can handle an Exception  
+ * 
+ *
+ */
 public interface ExceptionHandler {
+	
+	/**
+	 * Called when an exception occurs
+	 * @param e The occurred exception 
+	 */
 	void handleException(Exception e);
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/GridBagUtilities.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/GridBagUtilities.java	Tue Jul 10 22:39:37 2012 +0100
@@ -26,8 +26,6 @@
  * A Utility class providing static method to quickly arrange components, laid out by 
  * a GridBagLayout, in the following way: one component per row and 
  * either taking the whole column or just the right part of it, if preceded by a label.  
- *
- *
  */
 public class GridBagUtilities {
 	public GridBagUtilities(){
@@ -57,7 +55,8 @@
 	/**
 	 * Equivalent to {@link #label(int)} passing as argument the value previously 
 	 * set by {@link #setLabelPad(int)} or {@link #DEFAULT_LABEL_PAD} otherwise.
-	 * @return
+	 * 
+	 * @return a {@code GridBagConstrains} object to pass to the {@code add} method of {@code JComponent}
 	 */
 	public GridBagConstraints label(){
 		return label(labelPad);
@@ -72,6 +71,12 @@
 		this.labelPad = labelPad;
 	}
 	
+	/**
+	 * Provides the {@code GridBagConstrains} for a component placed on the same row 
+	 * and on the right of a label.
+	 *  
+	 * @return a {@code GridBagConstrains} object to pass to the {@code add} method of {@code JComponent}
+	 */
 	public GridBagConstraints field(){
 		GridBagConstraints c;
 		
@@ -85,6 +90,11 @@
 		return c;
 	}
 	
+	/**
+	 * Provides the {@code GridBagConstrains} for a component placed alone on a row.
+	 *  
+	 * @return a {@code GridBagConstrains} object to pass to the {@code add} method of {@code JComponent}
+	 */
 	public GridBagConstraints all(){
 		GridBagConstraints c;
 		
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/InteractionLog.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/InteractionLog.java	Tue Jul 10 22:39:37 2012 +0100
@@ -33,30 +33,42 @@
  * relevant actions. 
  */
 public class InteractionLog {
+	/**
+	 * Enable the logging 
+	 * @param logFileDir the path of the directory where the log file will be saved  
+	 * @throws IOException if a I/O problem occurs when writing log entry to the log file 
+	 */
 	public static void enable(String logFileDir) throws IOException{
 		logger.setLevel(Level.FINE);
 		logger.setUseParentHandlers(false);
 		if(fileHandler == null){
 			fileHandler = new FileHandler(logFileDir+System.getProperty("file.separator")+"interaction%u.log",true);
-			fileHandler.setFormatter(new CCmILogFormatter());
+			fileHandler.setFormatter(new InteractionLogFormatter());
 			logger.addHandler(fileHandler);
+
+			/* also print the log on the console */
+			java.util.logging.ConsoleHandler ch = new java.util.logging.ConsoleHandler();
+			ch.setLevel(Level.ALL);
+			ch.setFormatter(new InteractionLogFormatter());
+			logger.addHandler(ch);
 		}
-		
-		/* also print the log on the console */
-		java.util.logging.ConsoleHandler ch = new java.util.logging.ConsoleHandler();
-		ch.setLevel(Level.ALL);
-		ch.setFormatter(new CCmILogFormatter());
-		logger.addHandler(ch);
 	}
 	
+	/**
+	 * Disable the logging 
+	 */
 	public static void disable(){
 		logger.setLevel(Level.OFF);
 	}
 	
-	public static CCmILogFormatter newFormatter(){
-		return new CCmILogFormatter();
-	}
-	
+	/**
+	 * Logs a log entry in the file. Log entries are action that occurred during 
+	 * the interaction by local or remote user.   
+	 * 
+	 * @param source the source of the interaction 
+	 * @param action the occurred action 
+	 * @param args further informations about the occurred action 
+	 */
 	public static void log(String source, String action, String args){
 		StringBuilder builder = new StringBuilder(source);
 		builder.append(SEPARATOR)
@@ -66,14 +78,19 @@
 
 		logger.fine(builder.toString());
 	}
-	
+
+	/**
+	 * Logs a general message in the log file. This log entries are not 
+	 * linked to specific action of a local or remote user. 
+	 * @param msg the message to log 
+	 */
 	public static void log(String msg){
 		logger.config(msg);
 	}
 	
-	public static class CCmILogFormatter extends Formatter{
+	private static class InteractionLogFormatter extends Formatter{
 
-		private CCmILogFormatter(){
+		private InteractionLogFormatter(){
 			super();
 		}
 		
@@ -99,7 +116,11 @@
 			return builder.toString();
 		}
 	}
-	
+
+	/**
+	 * Release allocated resources. to be called hwen the interaction log is 
+	 * no longer needed.  
+	 */
 	public static void dispose(){
 		if(fileHandler != null)
 			fileHandler.close();
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/Pair.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/Pair.java	Tue Jul 10 22:39:37 2012 +0100
@@ -64,6 +64,12 @@
 		return true;
 	}
 
+	/**
+	 * the first item of the pair 
+	 */
 	public T1 first;
+	/**
+	 * the second item of the pair 
+	 */
 	public T2 second;
 }
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/PreferencesService.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/PreferencesService.java	Tue Jul 10 22:39:37 2012 +0100
@@ -38,10 +38,8 @@
          return service;
       }
       catch (SecurityException exception){
-         // that happens when we run under Web Start         
+         throw new RuntimeException(exception);      
       }
-      
-      return new NullPreferencesService();
    }
    
    /**
@@ -65,27 +63,15 @@
  * The default preferences service that uses the java.util.prefs API. 
  */
 class DefaultPreferencesService extends PreferencesService{   
-   /**
-    * Gets an instance of the service, suitable for the package of the given class.
-    * @param appClass the main application class (only the package name is used as the path to  
-    * app-specific preferences storage)
-    * @return an instance of the service
-    */
-   public DefaultPreferencesService()
-   {
-      prefs = Preferences.userNodeForPackage(this.getClass());   
-   }
-   
-   public String get(String key, String defval) { return prefs.get(key, defval); }
-   public void put(String key, String defval) { prefs.put(key, defval); }
 
-   private Preferences prefs;
+	public DefaultPreferencesService(){
+		prefs = Preferences.userNodeForPackage(this.getClass());   
+	}
+
+	@Override
+	public String get(String key, String defval) { return prefs.get(key, defval); }
+	@Override
+	public void put(String key, String defval) { prefs.put(key, defval); }
+
+	private Preferences prefs;
 }
-
-/**
- * The null preferences service that is returned when we are an applet. 
- */
-class NullPreferencesService extends PreferencesService {   
-   public String get(String key, String defval) { return defval; }
-   public void put(String key, String defval) { }
-}
\ No newline at end of file
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/ResourceFileWriter.java	Tue May 29 15:32:19 2012 +0100
+++ b/java/src/uk/ac/qmul/eecs/ccmi/utils/ResourceFileWriter.java	Tue Jul 10 22:39:37 2012 +0100
@@ -26,36 +26,54 @@
 import java.net.URL;
 
 /**
- * This class is used to extract a native library (e.g. .dll file in windows) from within a jar 
- * to the local file system, in order to allow the virtual machine to load it.
- *
+ * This class is used to store a resource (e.g. .dll file in windows)to the local file system.
+ * 
+ * This class can be used to allow the virtual machine to load resources which come embedded  
+ * into a jar file.
  */
 public class ResourceFileWriter {
 	
 	/**
-	 * Creates an instance of the the class linked to a native library file.
-	 * @param resource the URL of the native library file. The URL can be obtained by  
-	 * @see Class#getResource(String), therefore can be called from a class within a jar file
-	 * which needs to access a static library.  
+	 * Creates an instance of the the class linked to no resource. an resource file writer 
+	 * created with this constructor is useless, unless {@code setResource} is called before
+	 * writing the resource on the disk.
+	 * 
+	 * @see Class#getResource(String)  
+	 */
+	public ResourceFileWriter(){
+	}
+	
+	/**
+	 * Creates an instance of the the class linked to a specific resource 
+	 * 
+	 * @param resource the resource URL. The URL can be obtained by  
+	 * {@code getResource}, therefore can be called from a class within a jar file
+	 * which needs to access embedded resources.  
 	 */
 	public ResourceFileWriter(URL resource){
 		this.resource = resource;
 	}
 	
-	public void serResource(URL resource){
+	/**
+	 * Sets a new resource for this resource file writer. Successive calls to {@code writeToDisk} 
+	 * will store the resouce passed as argument in the file system. 
+	 * 
+	 * @param resource the URL of the new resource this resource file writer is linked to 
+	 */
+	public void setResource(URL resource){
 		this.resource = resource;;
 		path = null;
 	}
 
 	/**
-	 * Writes the file in a directory returned by {@link PreferencesService#get(String, String)}} if defined, or 
-	 * the System default temporary directory otherwise. 
-	 * The path to the file can be retrieved by @see {@link #getFilePath()} and then passed as argument 
-	 * to {@link System#load(String)} 
+	 * Writes the resource in the file passed as argument.
 	 * 
-	 * @param prefix a prefix the temporary native library file will have in the temporary directory  
+	 * The path to the file can be retrieved afterwards through @see {@link #getFilePath()} 
+	 * 
+	 * @param dir the directory where the resource will be saved
+	 * @param fileName the name of the file the resoruce will be saved in 
 	 */
-	public void writeToDisk(String dir,String fileName){
+	public void writeOnDisk(String dir,String fileName){
 		if (resource == null) 
 			return;
 		InputStream in = null;
@@ -78,9 +96,9 @@
 			path = null;
 		}finally{
 			if(in != null) 
-				try{in.close();}catch(IOException ioe){}
+				try{in.close();}catch(IOException ioe){ioe.printStackTrace();}
 			if(out != null) 
-				try{out.close();}catch(IOException ioe){}	
+				try{out.close();}catch(IOException ioe){ioe.printStackTrace();}	
 		}
 		
 	}
@@ -88,7 +106,9 @@
 	/**
 	 * Returns the absolute path of the last written file. If the writing wasn't successfully 
 	 * or no writing took place yet, then {@code null} is returned. 
-	 * @return the path of the last written file or {@code null}
+	 * 
+	 * @return the path of the last written file or {@code null} if no resource was set for this 
+	 * resource file writer or {@code writeToDisk}  was unsuccessful
 	 */
 	public String getFilePath(){
 		return path;
--- a/java/src/uk/ac/qmul/eecs/ccmi/utils/Validator.java	Tue May 29 15:32:19 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*  
- CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
-  
- 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.utils;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Validator {
-	
-	public static boolean validateIPAddr(String addr){
-		Matcher m = ip.matcher(addr);
-		return m.matches();
-	}
-	
-	private static Pattern ip = Pattern.compile("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b");
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/CollectionsManager.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,277 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+
+#include "CollectionsManager.h"
+
+using namespace H3DUtil::ArithmeticTypes;
+
+void CollectionsManager::init(void){
+	/**************** init jni variables **********************/
+	// --- classes --- 
+	// get the java haptics class 
+	hapticClass = env->GetObjectClass(*haptics);
+	if(hapticClass == NULL){
+		stopExecution("Could not find the Haptics class");
+	}
+	
+	bitsetClass = env->FindClass("Ljava/util/BitSet;");
+	if(bitsetClass == NULL)
+		stopExecution("failed to find bitset class");
+
+	//get the node list class 
+	listClass = env->FindClass("Ljava/util/ArrayList;");
+	if(listClass == NULL)
+		stopExecution("failed to find list class");
+
+	// get the Node class to call the get method on the node list 
+	hapticNodeClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/Node;");
+	if(hapticNodeClass == NULL){
+		stopExecution("Could not find the Node class");
+	}
+
+	// get the Edge class to call the get method on the edge list 
+	hapticEdgeClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/Edge;");
+	if(hapticEdgeClass == NULL){
+		stopExecution("Could not find the Edge class");
+	}
+	// --- methods ---
+	// "get" method id 
+	getMethodId = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");
+	if(getMethodId == NULL)
+		stopExecution("Could not retrieve the get method id");
+
+	//size method id
+	sizeMethodId = env->GetMethodID(listClass, "size", "()I");
+	if(sizeMethodId == NULL)
+		stopExecution("failed to get size method id");
+
+	//get method of the BitSet class
+	getBitMethodId = env->GetMethodID(bitsetClass, "get","(I)Z");
+	if(getBitMethodId == NULL)
+		stopExecution("failed to get the get method id of the BitSet class");
+
+	// -- field id's --
+	//retrieve edgeList field id
+	edgeListfieldId = env->GetFieldID(hapticClass,"currentEdges", "Ljava/util/ArrayList;");
+	if(edgeListfieldId == NULL){
+		stopExecution("failed to find the edge list field id"); 
+	}
+	
+	//retrieve nodeList field id
+	nodeListfieldId = env->GetFieldID(hapticClass,"currentNodes", "Ljava/util/ArrayList;");
+	if(nodeListfieldId == NULL){
+		stopExecution("failed to find the node list field id");
+	}
+
+	// Node fields id's 
+	xFieldId = env->GetFieldID(hapticNodeClass,"x","D");
+	if(xFieldId == NULL)
+		stopExecution("Could not find the x field ID");
+	
+
+	yFieldId = env->GetFieldID(hapticNodeClass,"y","D");
+	if(yFieldId == NULL)
+		stopExecution("Could not find the y field ID");
+	
+	nodeHapticIdFieldId = env->GetFieldID(hapticNodeClass, "hapticId", "I");
+	if(nodeHapticIdFieldId == NULL)
+		stopExecution("Could not find the node hapticId field ID");
+
+	nodeDiagramIdFieldId = env->GetFieldID(hapticNodeClass, "diagramId", "I");
+	if(nodeDiagramIdFieldId == NULL) 
+		stopExecution("Could not find the node diagramId field ID");
+	
+	// Edge field id's
+	// edge.hapticId
+	edgeHapticIdFieldId = env->GetFieldID(hapticEdgeClass, "hapticId", "I");
+	if(edgeHapticIdFieldId == NULL)
+		stopExecution("Could not find the edge hapticId field ID");
+
+	edgeDiagramIdFieldId = env->GetFieldID(hapticEdgeClass, "diagramId", "I");
+	if(edgeDiagramIdFieldId == NULL)
+		stopExecution("Could not find the edge diagramId field ID");
+
+	// stipplePattern
+	stipplePatternfieldId = env->GetFieldID(hapticEdgeClass, "stipplePattern", "I");
+	if(stipplePatternfieldId == NULL){
+		stopExecution("Could not find the stipplePattern field ID");
+	}
+
+	edgeSizefieldId = env->GetFieldID(hapticEdgeClass, "size", "I");
+	if(edgeSizefieldId == NULL)
+		stopExecution("Could not find edge size field ID");
+
+	edgeXsFieldId = env->GetFieldID(hapticEdgeClass,"xs", "[D");
+	if(edgeXsFieldId == NULL)
+		stopExecution("Could not find edge xs field ID");
+	
+	edgeYsFieldId = env->GetFieldID(hapticEdgeClass,"ys", "[D");
+	if(edgeYsFieldId == NULL)
+		stopExecution("Could not find edge ys field ID");
+
+	edgeAdjMatrixFieldId = env->GetFieldID(hapticEdgeClass,"adjMatrix", "[Ljava/util/BitSet;");
+	if(edgeAdjMatrixFieldId == NULL)
+		stopExecution("Could not find edge adjMatrix field ID");
+
+	attractPointXFieldId = env->GetFieldID(hapticEdgeClass, "attractPointX", "D");
+	if(attractPointXFieldId == NULL) 
+		stopExecution("Could not find the edge attractPointX field ID");
+
+	attractPointYFieldId = env->GetFieldID(hapticEdgeClass, "attractPointY", "D");
+	if(attractPointYFieldId == NULL) 
+		stopExecution("Could not find the edge attractPointY field ID");
+
+	edgeNodeStartFieldId = env->GetFieldID(hapticEdgeClass, "nodeStart", "I");
+	if(edgeNodeStartFieldId == NULL)
+		stopExecution("Could not find the edge nodeStart field ID");
+}
+
+const jint CollectionsManager::getNodesNum() {
+	/* to read the node list field into the nodeList variable each time we get the size is     *
+	 * needed as, when the tab is switched the nodeList variables still points to the previuos *
+	 * tab's node list. We do it only here and not in getNodeData because  getNodesData        *
+	 * follows this call before releasing the monitor, therefore data integrity is granted.    */
+	env->DeleteLocalRef(nodeList);
+	nodeList = env->GetObjectField(*haptics, nodeListfieldId);
+	if(nodeList == NULL){
+		stopExecution("could not get the node list field of Haptic Class");
+	}
+	jint size =  env->CallIntMethod( nodeList, sizeMethodId);
+	checkExceptions(env,"Could not call ArrayList<Node>.size()");
+	return size;
+}
+
+const jint CollectionsManager::getEdgesNum(){
+	env->DeleteLocalRef(edgeList);
+	edgeList = env->GetObjectField(*haptics, edgeListfieldId);
+	if(edgeList == NULL){
+		stopExecution("could not get the edge list field of Haptic Class");
+	}
+
+	jint size = env->CallIntMethod( edgeList, sizeMethodId);
+	checkExceptions(env, "Could not call ArrayList<Edge>.size()");
+	return size;
+}
+
+CollectionsManager::NodeData & CollectionsManager::getNodeData(const int i){
+	/* get the i-th node */
+	jobject currentNode = env->CallObjectMethod(nodeList,getMethodId,i);
+	checkExceptions(env,"Could not call ArrayList<Node>.size()");
+	fillupNodeData(nd,currentNode); 
+	env->DeleteLocalRef(currentNode);
+	return nd;
+}
+
+CollectionsManager::EdgeData & CollectionsManager::getEdgeData(const int i){
+	/* first we look for the i-th edge in the Haptics java class. Once we get it,  *
+	 * we need all the coordinates of the node this edge is connecting, so that we *
+	 * can draw it. The edge mantains a list of references to such nodes.          */
+
+	/* get the i-th edge */
+	jobject currentEdge = env->CallObjectMethod(edgeList,getMethodId,i);
+	checkExceptions(env, "Could not call ArrayList<Edge>.get(int) in the haptics edges");
+	if(currentEdge == NULL)
+		stopExecution("Could not find get current Edge");
+	jint size = env->GetIntField(currentEdge, edgeSizefieldId);
+	ed.setSize(size); 
+	fillupEdgeData(ed,currentEdge);	
+	env->DeleteLocalRef(currentEdge);
+	return ed;
+}
+
+void CollectionsManager::fillupNodeData(NodeData & nd, jobject & currentNode){
+	//reads the fields of the current node 
+	nd.x = env->GetDoubleField(currentNode,xFieldId);
+	nd.y = env->GetDoubleField(currentNode,yFieldId);
+	// takes coordinates from the screen. Needs to convert the y axis as in openGL (0,0) = bottom left corner
+	Vec3d & glCoordinatePosition = screenToHapticSpace(Vec3d(nd.x, nd.y, 0),screenWidth,screenHeight);
+	nd.x = glCoordinatePosition[0];
+	nd.y = glCoordinatePosition[1];
+	nd.hapticId = env->GetIntField(currentNode, nodeHapticIdFieldId);
+	nd.diagramId = env->GetIntField(currentNode, nodeDiagramIdFieldId);
+}
+
+void CollectionsManager::fillupEdgeData(EdgeData & ed, jobject & currentEdge){
+	/* get the array of x coordinates */
+	jobject xsAsObj = env->GetObjectField(currentEdge,edgeXsFieldId);
+	if(xsAsObj == NULL)
+		stopExecution("Cannot get the xs field");
+	jdoubleArray *jxs = reinterpret_cast<jdoubleArray*>(&xsAsObj);
+	double * xs = env->GetDoubleArrayElements(*jxs, NULL);
+	if(xs == NULL)
+		stopExecution("Cannot get the xs field array of double");
+
+	/* get the array of y coordinates */
+	jobject ysAsObj = env->GetObjectField(currentEdge,edgeYsFieldId);
+	if(ysAsObj == NULL)
+		stopExecution("Cannot get the xs field");
+		
+	jdoubleArray *jys = reinterpret_cast<jdoubleArray*>(&ysAsObj);
+	double * ys = env->GetDoubleArrayElements(*jys, NULL);
+	if(ys == NULL)
+		stopExecution("Cannot get the ys field array of double");
+	// copy the data into the edgeData object
+	for(unsigned int i=0; i<ed.getSize(); i++){ 
+		ed.x[i] = xs[i];
+		ed.y[i] = ys[i];
+		// takes coordinates from the screen (needs to convert the y axis as in openGL 0 = bottom left corner
+		Vec3d & glCoordinatePosition = screenToHapticSpace(Vec3d(ed.x[i], ed.y[i], 0),screenWidth,screenHeight);
+		ed.x[i] = glCoordinatePosition[0];
+		ed.y[i] = glCoordinatePosition[1];
+	}
+	env->ReleaseDoubleArrayElements(*jxs, xs, 0);
+	env->ReleaseDoubleArrayElements(*jys, ys, 0);
+	env->DeleteLocalRef(xsAsObj);
+	env->DeleteLocalRef(ysAsObj);
+	jobject adjMatrixAsObj = env->GetObjectField(currentEdge, edgeAdjMatrixFieldId);
+	if(adjMatrixAsObj == NULL)
+		stopExecution("Cannot get the adjMatrix field");
+	jobjectArray *jadjMatrix = reinterpret_cast<jobjectArray*>(&adjMatrixAsObj);
+	
+	for(unsigned int i=0; i<ed.getSize(); i++){
+		jobject adjMatrixBitSet = env->GetObjectArrayElement(*jadjMatrix,i);
+		if(adjMatrixBitSet == NULL)
+			stopExecution("Cannot get the adjMatrix field array element");
+		for(unsigned int j=0;j<ed.getSize(); j++){
+			jboolean b = env->CallBooleanMethod(adjMatrixBitSet,getBitMethodId,j);
+			checkExceptions(env,"Could not call BitSet.get()");
+			if(b == JNI_TRUE)
+				ed.adjMatrix[i][j] = true;
+			else
+				ed.adjMatrix[i][j] = false;
+		}
+		env->DeleteLocalRef(adjMatrixBitSet);
+	}	
+	env->DeleteLocalRef(adjMatrixAsObj);
+
+	// set the haptic id used by the haptic device 
+	ed.hapticId = env->GetIntField(currentEdge, edgeHapticIdFieldId);
+	// set the diagram id used in the java thread
+	ed.diagramId = env->GetIntField(currentEdge, edgeDiagramIdFieldId);
+	// set the stipple pattern 
+	ed.stipplePattern = env->GetIntField(currentEdge, stipplePatternfieldId);
+	// set the attract point
+	ed.attractPoint[0] = env->GetDoubleField(currentEdge, attractPointXFieldId);
+	ed.attractPoint[1] = env->GetDoubleField(currentEdge, attractPointYFieldId);
+	Vec3d & glCoordinatePosition = screenToHapticSpace(Vec3d(ed.attractPoint[0], ed.attractPoint[1], 0),screenWidth,screenHeight);
+	ed.attractPoint[0] = glCoordinatePosition[0];
+	ed.attractPoint[1] = glCoordinatePosition[1];
+	//set the index from which the nodes start in the adjMatrix
+	ed.nodeStart = env->GetIntField(currentEdge, edgeNodeStartFieldId);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/CollectionsManager.h	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,156 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/ 
+
+#pragma once
+#include <jni.h>
+#include <new>
+#include <H3DUtil/Vec3d.h>
+
+#include "utils.h"
+
+
+/* this class uses the java haptic object lists to provide nodes *
+ * and edges to draw to the graphic and haptic managers          */
+class CollectionsManager {
+public:
+	struct NodeData {
+		jdouble x;
+		jdouble y;
+		jint hapticId;
+		jint diagramId;
+
+		NodeData(){}
+	private :
+		NodeData(const NodeData& n){/* avoid mistakenly copy construction calls */}
+	};
+
+	struct EdgeData{
+		jdouble *x;
+		jdouble *y;
+		bool **adjMatrix;
+		jint hapticId;
+		jint diagramId;
+		jint stipplePattern;
+		jsize nodeStart;
+		jdouble attractPoint[2];
+		void setSize(unsigned int s){
+			size = s;
+			if(s > previousSize){
+				/* delete the old memory */
+				delete [] x;
+				delete [] y;
+				for(unsigned int i=0; i<previousSize; i++)
+					delete[] adjMatrix[i];
+				delete [] adjMatrix;
+				/* allocates a bigger one */
+				try{
+					x = new jdouble[size];
+					y = new jdouble[size];
+					adjMatrix = new  bool* [size];
+					for(unsigned int i=0; i<size; i++){
+						adjMatrix[i] = new bool[size];
+						for(unsigned int j=0; j<size; j++)
+							adjMatrix[i][j] = false;
+					}
+				}catch(std::bad_alloc){
+					stopExecution("Could not allocate memory for the program.\nAborting...");
+				}
+				previousSize = size;
+			}
+		}
+
+		unsigned int getSize() const {
+			return size;
+		}
+
+		EdgeData(unsigned int s) : size(s), previousSize(s){
+			x = new jdouble[size];
+			y = new jdouble[size];
+			adjMatrix = new  bool* [size];
+			for(unsigned int i=0; i<size; i++){
+				adjMatrix[i] = new bool[size];
+				for(unsigned int j=0; j<size; j++)
+					adjMatrix[i][j] = false;
+			}
+		}
+		~EdgeData(){
+			delete [] x;
+			delete [] y;
+			for(unsigned int i=0; i<size; i++)
+				delete[] adjMatrix[i];
+			delete [] adjMatrix;
+		}
+	private :
+		unsigned int size;
+		unsigned int previousSize;
+		EdgeData(const EdgeData& e){ /* avoid mistakenly copy construction */}
+
+	};
+private:
+	JNIEnv *env;
+	jobject *haptics;
+
+	jclass hapticClass;
+	jclass hapticNodeClass;
+	jclass hapticEdgeClass;
+	jclass listClass;
+	jclass bitsetClass;
+	jobject nodeList;
+	jobject edgeList;
+	jmethodID getMethodId;
+	jmethodID sizeMethodId;
+	jmethodID getBitMethodId;
+	jfieldID xFieldId;
+	jfieldID yFieldId;
+	jfieldID nodeHapticIdFieldId;
+	jfieldID edgeHapticIdFieldId;
+	jfieldID nodeDiagramIdFieldId;
+	jfieldID edgeDiagramIdFieldId;
+	jfieldID nodeListfieldId;
+	jfieldID stipplePatternfieldId;
+	jfieldID edgeListfieldId;
+	jfieldID edgeSizefieldId;
+	jfieldID edgeXsFieldId;
+	jfieldID edgeYsFieldId;
+	jfieldID edgeAdjMatrixFieldId;
+	jfieldID attractPointXFieldId;
+	jfieldID attractPointYFieldId;
+	jfieldID edgeNodeStartFieldId;
+
+	
+	int screenHeight;
+	int screenWidth;
+	NodeData nd;
+	EdgeData ed;
+	
+	void fillupNodeData(struct NodeData & nd, jobject & currentNode);
+	void fillupEdgeData(struct EdgeData & ed, jobject & currentEdge);
+public:
+	CollectionsManager(JNIEnv *environment, jobject *obj) : env(environment), haptics(obj), ed(2){}
+	virtual ~CollectionsManager(void){}
+	const jint getNodesNum();
+	const jint getEdgesNum();
+	struct NodeData & getNodeData(const int i);
+	struct EdgeData & getEdgeData(const int i);
+	inline int getScreenHeight() const { return screenHeight;}
+	inline void setScreenHeight(const int sh) { screenHeight = sh; }
+	inline int getScreenWidth() const { return screenWidth;}
+	inline void setScreenWidth(const int sw) { screenWidth = sw; }
+	void init(void);
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/FalconHaptics.vcproj	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="FalconHaptics"
+	ProjectGUID="{1639E3C7-81A2-4887-B953-DB4DB44566DA}"
+	RootNamespace="FalconHaptic"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_25\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_25\include\win32&quot;;C:\H3D\H3DUtil\include;C:\H3D\External\include\pthread;C:\H3D\HAPI\include;C:\H3D\External\include"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				WarningLevel="3"
+				DebugInformationFormat="4"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="C:\H3D\External\lib\freeglut.lib C:\H3D\lib\H3DUtil_vc9.lib C:\H3D\External\lib\pthreadVC2.lib C:\H3D\lib\HAPI_vc9.lib"
+				GenerateDebugInformation="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			CharacterSet="2"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				EnableIntrinsicFunctions="true"
+				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_25\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_25\include\win32&quot;;C:\H3D\H3DUtil\include;C:\H3D\External\include\pthread;C:\H3D\HAPI\include;C:\H3D\External\include"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="C:\H3D\External\lib\freeglut.lib C:\H3D\lib\H3DUtil_vc9.lib C:\H3D\External\lib\pthreadVC2.lib C:\H3D\lib\HAPI_vc9.lib"
+				AdditionalLibraryDirectories=""
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\CollectionsManager.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\HapticManager.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\utils.cpp"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+			<File
+				RelativePath=".\CollectionsManager.h"
+				>
+			</File>
+			<File
+				RelativePath=".\HapticManager.h"
+				>
+			</File>
+			<File
+				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.h"
+				>
+			</File>
+			<File
+				RelativePath=".\utils.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/HapticManager.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,550 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+
+#include "HapticManager.h"
+
+using namespace HAPI;
+
+// Render a sphere using OpenGL calls.
+void drawSphere() {
+  GLUquadricObj* pObj = gluNewQuadric();
+  gluSphere(pObj, 0.0010, 10, 10);
+  gluDeleteQuadric(pObj );
+}
+
+HapticManager::HapticManager(CollectionsManager * cManager, 
+							 void (*func)(
+								const jchar cmd,
+								const jint ID,
+								const jdouble startx, 
+								const jdouble starty, 
+								const jdouble endx, 
+								const jdouble endy)
+							) 
+		: executeCommand(func), cm(cManager), force_effect(0), last_touched_point(NULL),
+		last_touched_line(NULL),magnetic_mode(LOOSE), pickup_mode(RELEASED), object_unselected(false),
+		magnetic_mode_changed(false), pickedup_point(NULL), pickedup_line(NULL) {}
+
+
+bool HapticManager::init (void){
+	
+	hd = new AnyHapticsDevice();
+	
+	// The haptics renderer to use.
+	hd->setHapticsRenderer( new HAPI::GodObjectRenderer() );
+	
+	/* init the device */
+	if(hd->initDevice() != HAPIHapticsDevice::SUCCESS){
+		return false;
+	}
+	
+	/* enable the device ( forces and positions will be updated) */
+	hd->enableDevice();
+	
+	return true;
+}
+
+/* draws the visual diagram every time it's called. The haptic diagram is redrawn only if *
+ * something has changed in the diagram ( redraw_haptic_scene = true ) as, unlike openGL, *
+ * it doesn't need to be drawn at each frame but only once                                */
+void HapticManager::drawDiagram(bool redraw_haptics_scene, bool pickup, int _attract_to ){
+
+	/* STOP_ATTRACTION means the object has been reached (or eventually deleted). The attraction *
+	 * force must therefore be stopped and a redrawing of the haptic forces is necessary         */
+	if(attraction_mode == STOP_ATTRACTION){
+		attraction_mode = NO_ATTRACTION;
+		attract_to = NO_ID;
+		redraw_haptics_scene = true;
+	}
+
+	/* _attract_to is different from NO_ID there is no attraction going on (attraction_mode = NO_ATTRACTION)*
+	 * It differs from STOP_ATTRACTION in that the haptic scene doesn't have to be repainted                */
+	if(_attract_to != NO_ID){
+		attract_to = _attract_to;
+		attraction_mode = ACTIVE;
+		redraw_haptics_scene = true;
+	}
+
+	if(magnetic_mode_changed){
+		redraw_haptics_scene = true;
+		magnetic_mode_changed = false;
+	}
+
+	if(pickup){
+		pickup_mode = START_DRAGGING;
+		redraw_haptics_scene = true;
+	}
+
+	/* wash before use */
+	if(redraw_haptics_scene){
+		point_set.clear();
+		line_set.clear();
+		point_id_map.clear();
+		line_id_map.clear();
+	}
+
+	/* --- draw the edges --- */
+	glPushAttrib(GL_ENABLE_BIT);
+	glColor3f(1.0f,0.0f,0.0f); // Red
+	glLineWidth(2.0);
+	glDisable(GL_LIGHTING);  
+	glEnable(GL_COLOR_MATERIAL);
+	int numEdges = cm->getEdgesNum();
+	for ( int i = 0; i < numEdges; i++){
+		/* draw the edge in openGL */
+		CollectionsManager::EdgeData & ed = cm->getEdgeData(i);
+		glPushAttrib(GL_ENABLE_BIT);
+		glLineStipple(1, ed.stipplePattern);
+		glEnable(GL_LINE_STIPPLE);
+		glBegin(GL_LINES);
+		for(unsigned int j = 0; j < ed.getSize(); j++){
+			for(unsigned int k = j; k < ed.getSize(); k++){
+				if(ed.adjMatrix[j][k]){
+					glVertex3d(ed.x[j]*2,ed.y[j]*GRAPHIC_SCALE,0);
+					glVertex3d(ed.x[k]*2,ed.y[k]*GRAPHIC_SCALE,0);				
+				}
+			}
+		}
+		glEnd();		
+		glPopAttrib();
+		/* update haptics edge line set if a change in the collection occurred */
+		if(redraw_haptics_scene){
+			/* build the vector with the edges*/
+			for(unsigned int j = 0; j < ed.getSize(); j++){
+				for(unsigned int k = j; k < ed.getSize(); k++){
+					if(ed.adjMatrix[j][k]){
+						line_set.push_back(Collision::LineSegment (
+							Vec3(ed.x[j],ed.y[j],0),
+							Vec3(ed.x[k],ed.y[k],0)
+						));
+					}
+				}
+			}
+
+			for(vector< HAPI::Collision::LineSegment>::iterator itr = line_set.begin(); itr != line_set.end(); itr++){
+				line_id_map.insert(pair<Collision::LineSegment*,int>(&(*itr),ed.hapticId));
+			}
+		}
+	}
+	glPopAttrib();
+
+	/* --- draw the nodes --- */
+	int numNodes = cm->getNodesNum();
+	glColor3f(1.0f, 1.0f, 1.0f); // white	
+	for( int i = 0; i < numNodes; i++){
+		/* draw the nodes in openGL */
+		glPushMatrix();
+		CollectionsManager::NodeData &nd = cm->getNodeData(i);
+		glTranslated(nd.x*GRAPHIC_SCALE,nd.y*GRAPHIC_SCALE, 0);
+		drawSphere();
+		glPopMatrix();
+
+		/* update haptics node line set if a change in the collection occurred */
+		if(redraw_haptics_scene){
+			point_set.push_back(Vec3(nd.x,nd.y,0));
+			point_id_map.insert(pair<Collision::Point*,int>(&(point_set.back()),nd.hapticId)) ;
+		}
+	}
+	
+	/* --- update the haptic force if a change in the collection occurred --- */
+	if(redraw_haptics_scene){
+		/* reset the effects and set up new one, otherwise they would accumulate */
+		hd->clearEffects();
+		/* first lines */
+		if(!line_set.empty()){
+			/* force according to the current attraction_mode */
+			int force_factor = (magnetic_mode == STICKY) ? LINE_FORCE_FACTOR_STICKY : LINE_FORCE_FACTOR_LOOSE;
+			/* if the user is dragging or looking for an object, the force *
+			 * factor is always loose until she drops or find the object   */
+			if(pickup_mode == DRAGGING || pickup_mode == START_DRAGGING || attraction_mode == ACTIVE){
+				force_factor = LOOSE;
+			}
+			
+			/* update the force */
+			force_effect.reset( new HapticShapeConstraint(new HapticLineSet( line_set, 0 ),force_factor));			
+			hd->addEffect(force_effect.get());
+
+			/* recalculate last_touched_line if it was != NULL as the pointed      *
+			   object is now destroyed after clear() and replaced with a new one  */
+			if(last_touched_line != NULL){
+				last_touched_line = NULL;
+				vector<Collision::LineSegment>::iterator itr; 
+				Vec3 closest_point, normal, tex_coord;
+				for( itr=line_set.begin(); itr != line_set.end(); itr++ ){
+					itr->closestPoint(proxy_pos,closest_point,normal,tex_coord);
+					if(pointsDistance(closest_point,proxy_pos) < LINE_COLLISION_THRESHOLD){
+						last_touched_line = &(*itr);
+						break;
+					}
+				}
+				if(last_touched_line == NULL){
+					/* touched line was != NULL and now is NULL. It means it has been deleted *
+					 * while being touched, therefore an unselect command must be issued      */
+					object_unselected = true;
+				}
+			}
+		}else if(last_touched_line != NULL){
+			/* if last_touuched_line was != null it means an edge has been deleted   * 
+			 * which was being touched, therefore un unselect command must be issued */
+			object_unselected = true;
+			/* if there are no lines there cannot be a last touched one */
+			last_touched_line = NULL;
+		}
+		
+		/* now points */
+		if(!point_set.empty()){
+			/* update the force */
+			force_effect.reset( new HapticShapeConstraint(new HapticPointSet( point_set, 0 ),POINT_FORCE_FACTOR ) );
+			hd->addEffect(force_effect.get());
+
+			/* recalculate lastTouchedNode if it was != NULL as the pointed object is now destroyed after clear() */
+			if(last_touched_point != NULL){
+				last_touched_point = NULL;
+				vector<Collision::Point>::iterator itr;
+				for(itr=point_set.begin(); itr != point_set.end(); itr++ ){
+					if(pointsDistance(itr->position,proxy_pos) < POINT_COLLISION_THRESHOLD ){
+						last_touched_point = &(*itr);
+						break;
+					}
+				}
+				if(last_touched_point == NULL){
+					/* touched point was != NULL and now is NULL. It means it has been deleted *
+					 * while being touched, therefore an unselect command must be issued       */
+					object_unselected = true;
+				}
+			}
+		}else if(last_touched_point != NULL){
+			/* if last_touuched_point was != null it means a node has been deleted   * 
+			 * which was being touched, therefore un unselect command must be issued */
+			object_unselected = true;
+			/* if there are no points there cannot be a last touched one */
+			last_touched_point = NULL;
+		}
+
+		if(pickup_mode == START_DRAGGING && attraction_mode == NO_ATTRACTION){
+			force_effect.reset(new HapticSpring(proxy_pos,SPRING_FORCE_FACTOR));
+			hd->addEffect(force_effect.get());
+			/* spring force is initialized not pickup_mode goes to DRAGGING*/
+			pickup_mode = DRAGGING;
+		}else if(attraction_mode == ACTIVE){
+			bool found = false;
+			/* let's look for the object into the nodes */
+			for(map<HAPI::Collision::Point*,int>::iterator itr = point_id_map.begin();itr != point_id_map.end(); itr++){
+				if( (*itr).second == attract_to ){
+					force_effect.reset(new HapticSpring(((*itr).first)->position,SPRING_FORCE_FACTOR));
+					hd->addEffect(force_effect.get());
+					found = true;
+					break;
+				}
+			}
+			/* if not found look into the edges */
+			if(!found){
+				for(map<HAPI::Collision::LineSegment*,int>::iterator itr = line_id_map.begin();itr != line_id_map.end(); itr++){
+					if( (*itr).second == attract_to ){
+						HAPI::Collision::LineSegment* line_ptr = (*itr).first;
+						force_effect.reset(new HapticSpring(
+							midPoint(line_ptr->start, line_ptr->end),
+							SPRING_FORCE_FACTOR));
+						hd->addEffect(force_effect.get());
+						found = true;
+						break;
+					}
+				}
+			}
+
+			if(!found){
+				/* element has been deleted before user could reach it: go back to normal */
+				attraction_mode = STOP_ATTRACTION;
+			}
+
+		}
+		/* just deallocate the memory for the last force effect */
+		force_effect.reset();
+		/* tranfer all the forces to the device */
+		hd->transferObjects();
+	}
+}
+
+
+void HapticManager::drawCursor(){
+	HAPIHapticsRenderer *hr = hd->getHapticsRenderer();
+	if( hr ) {
+		/* save the proxy pos in a global variable */
+		proxy_pos = static_cast< HAPIProxyBasedRenderer * >(hr)->getProxyPosition();
+
+		glPushMatrix();
+		glPushAttrib(GL_CURRENT_BIT | GL_ENABLE_BIT | GL_LIGHTING_BIT);
+		glTranslatef( (GLfloat)proxy_pos.x*GRAPHIC_SCALE,
+			(GLfloat)proxy_pos.y*GRAPHIC_SCALE,
+			(GLfloat)proxy_pos.z );
+		glEnable(GL_COLOR_MATERIAL);
+		glColor3f(0.0f, 0.5f, 1.0f);// Blue
+		drawSphere();
+		glPopAttrib();
+		glPopMatrix();
+	}
+}
+
+bool HapticManager::checkNodeCollision(){
+	/* if the proxy goes far enough from the last touched node, then * 
+	 * lastTouched node can be set back to NULL						 */
+	bool do_unselect = false;
+	if(last_touched_point != NULL){
+		if(pointsDistance(last_touched_point->position, proxy_pos) > POINT_COLLISION_THRESHOLD){
+			last_touched_point = NULL;
+			do_unselect = true;
+		}
+	}
+
+	/* find the closest point among those closer than POINT_COLLISION_THRESHOLD */
+	vector<Collision::Point>::iterator itr;
+	HAPI::Collision::Point* found_point = NULL;
+	double min_dist = POINT_COLLISION_THRESHOLD;
+	double dist; 
+	for(itr=point_set.begin(); itr != point_set.end(); itr++ ){
+		if((dist = pointsDistance(itr->position,proxy_pos)) < min_dist ){
+			found_point = &(*itr);
+			min_dist = dist;
+		}
+	}
+
+	if(found_point != NULL){ 
+		/* if the last touched point is the same, it means the cursor was           *  
+		 * already on it therefore do nothing as the node name is uttered only when *   
+		 * the proxy comes across it and not at each frame (it would make a mess)   */
+		if(last_touched_point != found_point){ 
+			last_touched_point = found_point;
+			/* Setting last_touched_line to NULL, means untouching the eventual touching line.    *
+			* When a node is touched, the connected lines are indeed untouched as only one object *
+			* at time can be touching the position proxy                                          */
+			last_touched_line = NULL;
+			int point_id = point_id_map[last_touched_point];
+			/* select the node for eventual highlighting int the java tree */
+			executeCommand(SELECT_CMD,point_id,0,0,0,0);
+			/* utter the name of the node */
+			executeCommand(SPEAK_NAME_CMD,point_id,0,0,0,0);
+			if(attraction_mode == ACTIVE && point_id == attract_to){
+				/* we reached the attracting node, stop the attraction force */
+				attraction_mode = STOP_ATTRACTION;
+			}
+		}
+		return true;
+	}
+
+	if(do_unselect)
+		executeCommand(UNSELECT_CMD,0,0,0,0,0);
+
+	return false;
+}
+
+bool HapticManager::checkEdgeCollision(){
+	Vec3 closest_point, normal, tex_coord;
+
+	/* id is taken from the last touched line is overwritten. To understand why suppose we have an edge     *
+	 * made of two lines A and B. When going from one line to another we don't have to utter the name (see  *
+	 * comment below. last_touched_line goes from a to NULL before being set to B. If lastTouchedLineId is  *
+	 * calculated before checking for NULL-ness it will become -1 and it won't be useful anymore to avoid   *
+	 * uttering the edge name when going from A to B. In this way instead proxy goes from A to B, the id is *
+	 * to A's, last_touched_line becomes NULL and then it becomes B, but the name is not uttered because id *
+	 * is the same. This is a very long comment. I want to finish the line to make it look better. bye bye  */
+	int lastTouchedLineId = (last_touched_line == NULL) ? - 1 : line_id_map[last_touched_line]; 
+
+	bool do_unselect = false;
+	/* if the proxy goes far enough from the last touched line, then last_touched_line can be set back to NULL */
+	if(last_touched_line != NULL){
+		last_touched_line->closestPoint(proxy_pos,closest_point,normal,tex_coord);
+		if(pointsDistance(closest_point, proxy_pos) > LINE_COLLISION_THRESHOLD){
+			last_touched_line = NULL;
+			do_unselect = true;
+		}
+	}
+
+	/* find the closest line to the proxy among those closer than LINE_COLLISION_THRESHOLD */
+	vector<Collision::LineSegment>::iterator itr; 
+	HAPI::Collision::LineSegment* found_line = NULL;
+	double min_dist = LINE_COLLISION_THRESHOLD;
+	double dist; 
+	for( itr=line_set.begin(); itr != line_set.end(); itr++ ){
+		itr->closestPoint(proxy_pos,closest_point,normal,tex_coord);
+		if((dist = pointsDistance(closest_point,proxy_pos)) < min_dist){
+			found_line = &(*itr);
+			min_dist = dist;
+		}
+	}
+
+	if(found_line != NULL){
+		last_touched_line = found_line;
+		/* proxy can touch only one object at time either line or point */
+		last_touched_point = NULL;
+		/* An edge can be broken into several lines. Such lines will map to the same edge id *
+		 * If the proxy detouches from a line but immediately touches another line mapped to *
+		 * the same id, it just means the proxy is going aling the line and therefore no     *
+		 * name must be uttered again (the name must be uttered when touching an edge and    *
+		 * not when touching each line. We keep track of the last touched id and if it's the *
+		 * as te new touched line id, then nothing is uttered, for we're on the same edge    */
+		if(lastTouchedLineId != line_id_map[last_touched_line]){
+			int line_id = line_id_map[last_touched_line];
+			/* select the node for eventual highlighting int the java tree */
+			executeCommand(SELECT_CMD,line_id,0,0,0,0);
+			/* utter the name of the edge */
+			executeCommand(SPEAK_NAME_CMD,line_id,0,0,0,0);
+			if(attraction_mode == ACTIVE && line_id == attract_to){
+				/* we reached the attracting node, stop the attraction force */
+				attraction_mode = STOP_ATTRACTION;
+			}
+		}
+		return true;
+	}
+
+	if(do_unselect){
+		executeCommand(UNSELECT_CMD,0,0,0,0,0);
+	}
+
+	return false;
+}
+
+bool HapticManager::checkUnselection(void){
+	if(object_unselected){
+		object_unselected = false;
+		executeCommand(UNSELECT_CMD,0,0,0,0,0);
+		return true;
+	}
+	return false;
+}
+
+bool HapticManager::checkButtons(void){
+	HAPIInt32 status = hd->getButtonStatus();
+	/* handle the buttons. Buttons can be pressed one at time. If a button is pressed all the    *
+     * other buttons (either when pressed or released) are ignored untill the button is released */
+	if(pressed_button == NO_BUTTON){
+		if(status & FRONT_BUTTON){ 
+			/* front button pressed, change how magnetic the lines are */ 
+			pressed_button = FRONT_BUTTON;
+			/* switch from sticky to loose and vice versa */
+			magnetic_mode = (magnetic_mode == STICKY) ? LOOSE : STICKY;
+			magnetic_mode_changed = true;
+			/* make a sound according to the new attraction mode */
+			executeCommand(PLAY_SOUND_CMD, (magnetic_mode == STICKY) ? STICKY_MODE_SOUND : LOOSE_MODE_SOUND ,0,0,0,0);
+			return true;
+		}else if(status & REAR_BUTTON){ 
+			pressed_button = REAR_BUTTON;
+			/* rear button pressed: First time it picks up an object, second time it drops it */ 
+			if(pickup_mode == RELEASED){ // pickup mode was released, then now user picked up an object 
+				if(last_touched_point != NULL){
+					pickedup_point = last_touched_point;
+					last_dragging_pos = pickedup_point->position; // last_dragging_pos used in checkMotion 
+					/* send a pickup command to the Java thread which will in turn issue another pickup   *
+					 * command to this thread (pickup = true in drawDiagram) if the lock could be granted */
+					executeCommand(PICKUP_CMD,point_id_map[last_touched_point],0,0,0,0);
+				}else if(last_touched_line != NULL){
+					pickedup_line = last_touched_line;
+					pickup_line_pos = proxy_pos; // the point where the line was picked up 
+					last_dragging_pos = proxy_pos;// last_dragging_pos used in checkMotion 
+					executeCommand(PICKUP_CMD,line_id_map[last_touched_line],0,0,0,0);
+				}/* else user picked up thin air. Do nothing.  */
+			}else if(pickup_mode == DRAGGING){ // pickup mode was dragging, then now user dropped an object 
+				pickup_mode = RELEASED;
+				Vec3 & new_position = hapticToScreenSpace(Vec3(proxy_pos),
+						cm->getScreenWidth(),
+						cm->getScreenHeight());
+				if(pickedup_point){
+					executeCommand(MOVE_CMD,point_id_map[pickedup_point],new_position.x,new_position.y,0,0);
+					pickedup_point = NULL;
+				}else{
+					hapticToScreenSpace(pickup_line_pos,
+						cm->getScreenWidth(),
+						cm->getScreenHeight());
+					executeCommand(MOVE_CMD,
+						line_id_map[pickedup_line],
+						new_position.x,
+						new_position.y,
+						pickup_line_pos.x,
+						pickup_line_pos.y);
+					pickedup_line = NULL;
+				}
+				return true;
+			}
+		} else if(status & ( LEFT_BUTTON | RIGHT_BUTTON )){ // either left or right button pressed
+			pressed_button = (status == LEFT_BUTTON) ? LEFT_BUTTON : RIGHT_BUTTON;
+			if(last_touched_point != NULL){ // priority to nodes 
+				executeCommand(SPEAK_INFO_CMD,point_id_map[last_touched_point], 0,0,0,0);
+				return true;
+			}
+			if(last_touched_line != NULL){
+				executeCommand(SPEAK_INFO_CMD,line_id_map[last_touched_line], 0,0,0,0);
+				return true;
+			}
+		}
+	} else if ( /* deactivate the buttons */
+		(pressed_button == FRONT_BUTTON && !(status & FRONT_BUTTON)) ||
+		(pressed_button == REAR_BUTTON && !(status & REAR_BUTTON))   ||
+		(pressed_button == LEFT_BUTTON && !(status & LEFT_BUTTON))   ||
+		(pressed_button == RIGHT_BUTTON && !(status & RIGHT_BUTTON))) 
+	{ 
+		pressed_button = NO_BUTTON;
+	} 
+	return false;
+}
+
+bool HapticManager::checkMotion(void){
+	if(pickup_mode != DRAGGING)
+		return false;
+	if(pointsDistance(proxy_pos,last_dragging_pos) > DRAGGING_SOUND_THRESHOLD){
+		last_dragging_pos = proxy_pos;
+		executeCommand(PLAY_SOUND_CMD,DRAGGING_SOUND,0,0,0,0);
+		return true;
+	}
+	return false;
+}
+
+void HapticManager::dispose(void){
+	hd->releaseDevice();
+	delete hd;
+}
+
+
+/* static constants initialization */
+const int HapticManager::NO_ID = 0;
+
+const int HapticManager::GRAPHIC_SCALE = 2; 
+const int HapticManager::SPRING_FORCE_FACTOR = 200;
+const int HapticManager::LINE_FORCE_FACTOR_STICKY = 2000;
+const int HapticManager::LINE_FORCE_FACTOR_LOOSE = 100;
+const int HapticManager::POINT_FORCE_FACTOR = 20;
+const double HapticManager::POINT_COLLISION_THRESHOLD = 0.0025;
+const double HapticManager::LINE_COLLISION_THRESHOLD = 0.0025;
+const double HapticManager::DRAGGING_SOUND_THRESHOLD = 0.01;
+
+const int HapticManager::LOOSE_MODE_SOUND = 0;
+const int HapticManager::STICKY_MODE_SOUND = 1;
+const int HapticManager::DRAGGING_SOUND = 3;
+
+const char HapticManager::MOVE_CMD = 'm';
+const char HapticManager::PLAY_SOUND_CMD = 'g';
+const char HapticManager::SPEAK_NAME_CMD = 't';
+const char HapticManager::PICKUP_CMD = 'c';
+const char HapticManager::SPEAK_INFO_CMD = 'i';
+const char HapticManager::SELECT_CMD = 's';
+const char HapticManager::UNSELECT_CMD = 'u';
+
+const HAPIInt32 HapticManager::FRONT_BUTTON = (1 << 2);
+const HAPIInt32 HapticManager::LEFT_BUTTON = (1 << 1);
+const HAPIInt32 HapticManager::RIGHT_BUTTON = (1 << 3);
+const HAPIInt32 HapticManager::REAR_BUTTON = (1 << 0);
+const HAPIInt32 HapticManager::NO_BUTTON = 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/HapticManager.h	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,130 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+#pragma once
+#include "CollectionsManager.h"
+#include <HAPI/AnyHapticsDevice.h>
+#include <HAPI/GodObjectRenderer.h>
+
+#include <HAPI/HapticPointSet.h>
+#include <HAPI/HapticLineSet.h>
+#include <HAPI/HapticSpring.h>
+#include <HAPI/HapticShapeConstraint.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glut.h>
+
+
+
+class HapticManager
+{
+	CollectionsManager *cm ;
+	void (*executeCommand)(const jchar cmd, const jint ID, const jdouble startx, const jdouble starty, const jdouble endx, const jdouble endy);
+	HAPI::AnyHapticsDevice *hd;
+	/* The force effect in use. Contains 0 if there is no force effect in use. */
+    H3DUtil::AutoRef<HAPI::HAPIForceEffect> force_effect;
+	/* the current set of haptic lines. It's used as argument when adding a constraint effect to the hapitc *
+	 * device and it's filled up with the edges of the current diagram through the collection manager       */
+	vector< HAPI::Collision::LineSegment> line_set;
+	vector<HAPI::Collision::Point> point_set;
+	map<HAPI::Collision::Point*,int> point_id_map;
+	map<HAPI::Collision::LineSegment*,int> line_id_map;
+	/* in order not to utter continuosly the touched node/edge name, keep track of the node/edge that was     *
+	 * last touched and avoid to utter its name unless the proxy touches another node/edge or goes far enough */
+	HAPI::Collision::Point* last_touched_point;
+	HAPI::Collision::LineSegment* last_touched_line;
+	HAPI::Vec3 proxy_pos;
+	HAPI::HAPIInt32 pressed_button;
+
+	enum {STICKY, LOOSE} magnetic_mode;
+	bool magnetic_mode_changed;
+
+	enum {START_DRAGGING, DRAGGING, RELEASED} pickup_mode;
+	HAPI::Collision::Point *pickedup_point;
+	HAPI::Collision::LineSegment *pickedup_line;
+	HAPI::Vec3 pickup_line_pos;
+	HAPI::Vec3 last_dragging_pos;
+
+	int attract_to;
+	enum {NO_ATTRACTION ,STOP_ATTRACTION, ACTIVE} attraction_mode;
+
+	bool object_unselected;
+	
+	/* how bigger graphic coordinates are than haptic coordinates */
+	static const int GRAPHIC_SCALE; 
+	static const int SPRING_FORCE_FACTOR;
+	static const int LINE_FORCE_FACTOR_LOOSE;
+	static const int LINE_FORCE_FACTOR_STICKY;
+	static const int POINT_FORCE_FACTOR;
+	static const double POINT_COLLISION_THRESHOLD;
+	static const double LINE_COLLISION_THRESHOLD;
+	static const double DRAGGING_SOUND_THRESHOLD;
+
+	/* commands sent to the java thread */
+	static const char MOVE_CMD;
+	static const char PLAY_SOUND_CMD;
+	static const char SPEAK_NAME_CMD;
+	static const char PICKUP_CMD;
+	static const char SPEAK_INFO_CMD;
+	static const char SELECT_CMD;
+	static const char UNSELECT_CMD;
+
+	/* arguments for PLAY_SOUND_CMD */
+	static const int LOOSE_MODE_SOUND;
+	static const int STICKY_MODE_SOUND;
+	static const int DRAGGING_SOUND;
+
+	static const HAPI::HAPIInt32 FRONT_BUTTON;
+	static const HAPI::HAPIInt32 LEFT_BUTTON;
+	static const HAPI::HAPIInt32 RIGHT_BUTTON;
+	static const HAPI::HAPIInt32 REAR_BUTTON;
+	static const HAPI::HAPIInt32 NO_BUTTON;
+public:
+	HapticManager(CollectionsManager * cManager, void (*func)(const jchar cmd, const jint ID, const jdouble startx, const jdouble starty, const jdouble endx, const jdouble endy)); 
+
+	/* try and initialize the haptic device, return false if the initialization fails */
+	bool init(void);
+
+	/* drawing routines (both graphically and haptically) */
+	void drawCursor(void);
+	void drawDiagram(bool redrawHapticsScene,bool pickup, int attract_to);
+
+	/* collisions routines: check if the proxy is touching a node or an edge  */
+	bool checkNodeCollision(void);
+	bool checkEdgeCollision(void);
+
+	/* check if an unselection command must be issued. Commands cannot be issued in the drawDiagram *
+	 * as it might possibly result in a deadlock. Therefore in drawDiagram the inner flag variable  *
+	 * object_unselected is set if necessary, and then when this method is called, the command is   *
+	 * issued if the flad was set to true */
+	bool checkUnselection(void);
+
+	/* check buttons routine */
+	bool checkButtons(void);
+
+	/* routine that plays chain sound during motion if an object is being dragged */
+	bool checkMotion();
+
+	/* release the allocated resources */
+	void dispose(void);
+
+	/* a null id value for Java nodes and edges. The id used for nodes and edges is *
+	 * the hashCode of the Java object, therefore a zero value will work for NO_ID  */
+	static const int NO_ID;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,401 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+
+#include "uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.h"
+#include "HapticManager.h"
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <setjmp.h>
+#include <GL/glut.h>
+#ifdef FREEGLUT
+#include <GL/freeglut.h>
+#endif
+#include "utils.h"
+
+/* enum to match against when checking for the jump result */
+enum {JMP_OK =0, JMP_EXIT };
+
+/* Functions prototypes. */
+/* callbacks */
+void display(void);
+void reshape(int width, int height);
+void idle(void);   
+void exitProcedure(void);
+void commandCallback(const jchar cmd, const jint ID, const jdouble x, const jdouble y,const jdouble startX, const jdouble startY);
+
+void initJniVariables(void);
+
+/* variables */
+static HapticManager *hManager;
+static CollectionsManager *cManager;
+static JNIEnv *env;
+static jobject *lock;
+static jmp_buf jmpenv;	
+static int jmpval = 0;
+static bool reshaped = false;
+/* jni variables */
+static jclass hapticClass;
+static jclass hapticListenerClass;
+static jfieldID shutdownFieldId;
+static jfieldID hapticInitFailedfieldId;
+static jfieldID collectionsChangedFieldId;
+static jfieldID pickUpFieldId;
+static jfieldID attractToFieldId;
+/* hapticListener jni variables */
+static jfieldID hapticListenerFieldId;
+static jfieldID cmdFieldId; // belongs to the haptic listener
+static jfieldID diagramElementFieldId; // belongs to the haptic listener
+static jfieldID xFieldId; // belongs to the haptic listener
+static jfieldID yFieldId; // belongs to the haptic listener
+static jfieldID startXFieldId; // belongs to the haptic listener
+static jfieldID startYFieldId; // belongs to the haptic listener
+static jobject hapticListener;
+/* synchronization methods ids */
+static jmethodID notifyMethodId;
+static jmethodID notifyListenerMethodId;
+static jmethodID waitMethodId;
+
+/* Native initialization method for the Falcon Haptic device. Upon successful initialization a loop *
+ * is started that communicates with the Java program in order to represent the diagram haptically  */
+JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_initFalcon
+  (JNIEnv *environment, jobject obj, jint w, jint h){
+
+	env = environment;
+	lock = &obj;
+	/* fake main argv and argc as this is a dll */
+	char *argv[1] = {"FalconHaptics"};
+	int argc = 1;
+	initJniVariables();
+
+	cManager = new CollectionsManager(env,lock);
+	cManager->init();
+	hManager = new HapticManager(cManager,commandCallback);
+
+	/* try to initialize the haptic, tell the java thread about the success/failure. The endeavour   *
+	 * takes the lock on the haptics java object in order to access the nodes and edges concurrently */
+	if(env->MonitorEnter(*lock) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic thread monitor");
+	}
+	checkExceptions(env, "Could not enter monitor on Haptics");
+
+	bool mustSayGoodbye = false;
+
+	if(!hManager->init()){
+		/* set the field in the java class to comunicate the main thread (waiting   *
+		 * for the initialization to complete) that the initialization failed       */
+		env->SetBooleanField(*lock,hapticInitFailedfieldId,JNI_TRUE);
+		mustSayGoodbye = true;
+	}
+
+	if(mustSayGoodbye)
+		std::cout << "Failed to initialize Falcon haptic device" << std::endl;
+	else
+		std::cout << "Falcon haptic device successfully initialized" << std::endl;
+
+	/* notify the java thread */
+	env->CallVoidMethod(*lock,notifyMethodId);
+	checkExceptions(env,"Could not call notify() on Haptics");
+	
+	/* release the lock */
+	if(env->MonitorExit(*lock) != JNI_OK){
+		std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
+		exit(-1);
+	}
+	
+	if(mustSayGoodbye)
+		/* initialization failed: return */
+		return -1;
+
+	/* haptic device is initialized, now build the openGL window */
+
+	/* glut initialization */
+    glutInit(&argc, argv);
+
+	glutInitWindowSize( w, h );
+	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
+	glutCreateWindow( "Falcon Haptics Window" );
+
+	/* set up GLUT callback functions */
+	glutDisplayFunc ( display  );
+	glutReshapeFunc ( reshape );
+	glutIdleFunc( idle );
+
+	/* use setjmp to be able to jump off the glutMainLoop when the user shuts the program down */
+	jmpval = setjmp(jmpenv);
+
+	/* start the loop*/
+	if(jmpval == JMP_OK){
+		glutMainLoop();
+	}
+
+	/* jmpval = JMP_EXIT, we're coming from the glut loop */
+	exitProcedure();
+
+	return 0;
+}
+
+/* GLUT display callback */
+void display(){ 
+	/* takes the lock on the haptics java obejct in order to access the nodes and edges concurrently */
+	if(env->MonitorEnter(*lock) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic thread monitor");
+	}
+	checkExceptions(env,"Could not enter monitor on Haptics");
+
+	/* check if there is a shutdown request */
+	if( env->GetBooleanField(*lock,shutdownFieldId) == JNI_TRUE ){
+		// notify the other thread that this thread is about to die
+		env->CallVoidMethod(*lock,notifyMethodId);
+		checkExceptions(env, "Could not call notify() on Haptics");
+		// release the lock 
+		if(env->MonitorExit(*lock) != JNI_OK){
+			std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
+		}
+		longjmp(jmpenv,JMP_EXIT);
+	}
+	
+	/* check if the node and edges collections have changed since the last frame or if      *
+	 * the window has been reshaped. In either case the haptic scene must be rendered again */
+	bool collectionsChanged = false;
+	if(env->GetBooleanField(*lock,collectionsChangedFieldId) == JNI_TRUE || reshaped){
+		if(reshaped)
+			reshaped = false;
+		/* remember that collections have changed (will be used later in hManager->drawDiagram) */
+		collectionsChanged = true;
+		/* set the Java boolean variable back to false */
+		env->SetBooleanField(*lock,collectionsChangedFieldId,JNI_FALSE);
+	}
+
+	/* check if the user picked up an object */
+	jboolean picked_up = env->GetBooleanField(*lock,pickUpFieldId);
+	if(picked_up == JNI_TRUE){
+		env->SetBooleanField(*lock,pickUpFieldId,JNI_FALSE);
+	}
+
+	/* check if the user asked to be attracted to a node */
+	jint attract_to = env->GetIntField(*lock,attractToFieldId);
+	if(attract_to != HapticManager::NO_ID){
+		env->SetIntField(*lock,attractToFieldId,HapticManager::NO_ID);
+	} 
+
+	/* --- start drawing --- */
+	/* Set up OpenGL state. */
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LESS);
+	glDepthMask(GL_TRUE);
+	glEnable(GL_CULL_FACE);
+	glShadeModel(GL_SMOOTH);
+	glEnable( GL_LIGHTING );
+	glEnable(GL_LIGHT0);
+	glLoadIdentity();
+
+	gluLookAt(0, 0, 0.1, // eye position .05
+		0, 0, 0,  // center position 
+		0, 1, 0); // which direction is up 
+	glEnable( GL_NORMALIZE );
+
+	/* draw the diagram graphicly and, if the collection has changed, haptically too */
+	hManager->drawCursor();
+	hManager->drawDiagram(collectionsChanged,(picked_up == JNI_TRUE),attract_to);
+	
+
+	/* release lock. HapticManager methods from now on can execute commands without deadlock risk */
+	if(env->MonitorExit(*lock) != JNI_OK){
+		stopExecution("Could not release memory for haptic thread monitor");
+	}
+	/* check button status */
+	hManager->checkButtons();
+	
+	/* check if un unselect command is to be issued, for an object being touched has been deleted */
+	hManager->checkUnselection();
+
+	/* check collisions. priority to nodes */
+	bool node_collision = hManager->checkNodeCollision();
+	if(!node_collision) // only one object at time can be touched 
+		hManager->checkEdgeCollision();
+	/* check motion feedback */
+	hManager->checkMotion();
+
+	/* swap graphic buffers. */
+	glutSwapBuffers();
+}
+
+/* GLUT reshape callback */
+void reshape (int w, int h){
+	cManager->setScreenHeight(h);
+	cManager->setScreenWidth(w);
+    reshaped = true;
+
+	static const double ANGLE = 100;
+	glViewport(0, 0, w, h);   
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	gluPerspective(ANGLE, // angle 
+		(float)w / h, // aspect ratio
+		0.01, // near clipping plane .01
+		500);// far clipping plane 1000
+	glMatrixMode(GL_MODELVIEW);
+}
+
+/* GLUT idle callback */
+void idle(){
+    glutPostRedisplay();
+}
+
+/* called before exit */
+void exitProcedure(){
+	hManager->dispose();
+	delete hManager;
+	delete cManager;
+}
+
+/* initialize all the variables needed for the jni access to the Falcon Haptics class by the Haptic thread*/
+void initJniVariables(void){
+	/* --- CLASSES --- */
+	//this class
+	hapticClass = env->GetObjectClass(*lock);
+	if(hapticClass == NULL){
+		stopExecution("Could not find the Haptics class");
+	}
+	// the haptic listener, member of this class
+	hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
+	if(hapticListenerClass == NULL){
+		stopExecution("Could not find the haptic listener class");
+	}
+
+	/* --- FIELD IDS --- */
+
+	// boolean set to this thread to notify the java thread the unsuccessful initialization of haptic device
+	hapticInitFailedfieldId = env->GetFieldID(hapticClass,"initFailed", "Z");
+	if(hapticInitFailedfieldId == NULL){
+		stopExecution("failed to find the initFailed field id");
+	}
+
+	// boolean set by the java thread to notify this thread the program has been shut down 
+	shutdownFieldId = env->GetFieldID(hapticClass, "shutdown", "Z");
+	if(shutdownFieldId == NULL){
+		stopExecution("failed to find the shutdownfieldId field id");
+	}
+
+	collectionsChangedFieldId = env->GetFieldID(hapticClass, "collectionsChanged", "Z");
+	if(collectionsChangedFieldId == NULL){
+		stopExecution("failed to find the collectionsChangedFieldId field id");
+	}
+
+	attractToFieldId = env->GetFieldID(hapticClass, "attractTo" , "I");
+	if(attractToFieldId == NULL){
+		stopExecution("failed to find the attractTo field id");
+	}
+
+	pickUpFieldId = env->GetFieldID(hapticClass,"pickUp","Z");
+	if(pickUpFieldId == NULL){
+		stopExecution("failed to find the pickUp field id");
+	}
+
+	hapticListenerFieldId = env->GetFieldID(hapticClass, "hapticListener", "Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
+	if(hapticListenerFieldId == NULL){
+		stopExecution("failed to find the hapticListenerThread field id");
+	}
+
+	cmdFieldId = env->GetFieldID(hapticListenerClass,"cmd", "C");
+	if(cmdFieldId == NULL){
+		stopExecution("failed to find the cmd field id of the hapticListener class");
+	}
+
+	diagramElementFieldId = env->GetFieldID(hapticListenerClass, "diagramElementID", "I");
+	if(diagramElementFieldId == NULL){
+		stopExecution("failed to find the diagramElement field id of the hapticListener class");
+	}
+
+	xFieldId = env->GetFieldID(hapticListenerClass, "x", "D");
+	if(xFieldId == NULL){
+		stopExecution("failed to find the x field id of the hapticListener class");
+	}
+
+	yFieldId = env->GetFieldID(hapticListenerClass, "y", "D");
+	if(yFieldId == NULL){
+		stopExecution("failed to find the y field id of the hapticListener class");
+	}
+
+	startXFieldId = env->GetFieldID(hapticListenerClass, "startX", "D");
+	if(startXFieldId == NULL){
+		stopExecution("failed to find the x field id of the hapticListener class");
+	}
+
+	startYFieldId = env->GetFieldID(hapticListenerClass, "startY", "D");
+	if(startYFieldId == NULL){
+		stopExecution("failed to find the y field id of the hapticListener class");
+	}
+
+	hapticListener = env->GetObjectField(*lock,hapticListenerFieldId);
+	/* --- SYNCHRONIZATION METHODS ID --- */
+	// notify()
+	notifyMethodId = env->GetMethodID(hapticClass,"notify","()V");
+	if(notifyMethodId == NULL){
+		stopExecution("failed to find the notify method id");
+	}
+
+	notifyListenerMethodId = env->GetMethodID(hapticListenerClass,"notify","()V");
+	if(notifyListenerMethodId == NULL){
+		stopExecution("failed to find the notify method id");
+	}
+	// wait()
+	waitMethodId = env->GetMethodID(hapticListenerClass,"wait","()V");
+	if(waitMethodId == NULL){
+		stopExecution("failed to find the wait method id");
+	}
+}
+
+void commandCallback(const jchar cmd, const jint ID, const jdouble x, const jdouble y, const jdouble startX, const jdouble startY){
+	/* the haptic listener java thread is waiting for commands
+	   first set the variable the Haptic Listener java thread will read after being notified,
+	   then notify and get it awake. Thus wait for the java thread to notify that the command 
+	   has been accomplished. This is done as otherwise some commands might be neglected. as if the thread 
+	   scheduler decides to execute twice this routine without executing the java thread in the middle then 
+	   the former command gets overwritten by the latter. 
+	   Note the monitor is hapticListener and not haptics as for the draw function. 
+	   When in this routine, this thread does not hold the lock on haptics as if a command results in changing
+	   the elements collections (e.g. moveNode) all those methods are synchronized and require to acquire the lock on haptics.
+	   Since this thread would wait for the command to be executed by the java thread, which in turns would wait for this
+	   thread to release the lock on haptics, that would result in a deadlock. 
+	 */
+	if(env->MonitorEnter(hapticListener) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic listener thread monitor");
+	}
+	checkExceptions(env,"Could not enter monitor on the haptic listener");
+	/* Fill all the shared variables for informaton for the command to be executed */
+	env->SetCharField(hapticListener,cmdFieldId,cmd);
+	env->SetIntField(hapticListener,diagramElementFieldId,ID);
+	env->SetDoubleField(hapticListener,xFieldId,x);
+	env->SetDoubleField(hapticListener,yFieldId,y);
+	env->SetDoubleField(hapticListener,startXFieldId,startX);
+	env->SetDoubleField(hapticListener,startYFieldId,startY);
+	// wake the java thread up to execute the command
+	env->CallVoidMethod(hapticListener,notifyListenerMethodId);
+	checkExceptions(env, "Could not call notify() on HapticListener");
+	/* wait for the commands to be executed. Here is actually where the monitor is   *
+	 * freed and the java thread starts to execute the command, having been notified */ 
+	env->CallVoidMethod(hapticListener,waitMethodId);
+	checkExceptions(env, "Could not call wait() on HapticListener");
+	if(env->MonitorExit(hapticListener) != JNI_OK){
+		stopExecution("Could not release memory for haptic listener thread monitor");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics.h	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,45 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/
+
+#include <jni.h>
+/* Header for class uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics */
+
+#ifndef _Included_uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics
+#define _Included_uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_MIN_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_MIN_PRIORITY 1L
+#undef uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_NORM_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_NORM_PRIORITY 5L
+#undef uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_MAX_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_MAX_PRIORITY 10L
+/*
+ * Class:     uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics
+ * Method:    init
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_FalconHaptics_initFalcon
+  (JNIEnv *, jobject, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/utils.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,75 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+
+#include "utils.h"
+
+#define MAX(a,b) ((a) < (b) ? (b) : (a))
+#define MIN(a,b) ((a) > (b) ? (b) : (a))
+
+void checkExceptions(JNIEnv *env, char* what){
+	if(env->ExceptionOccurred()){
+		std::cout << "Exception occurred!!!" << std::endl;
+		std::cout << what << std::endl;
+		env->ExceptionDescribe();
+		exit(-1);
+	}
+}
+
+void stopExecution(char* msg){
+	std::cerr << msg << std::endl;
+	exit(-1);
+}
+
+H3DUtil::ArithmeticTypes::Vec3d & screenToHapticSpace(H3DUtil::ArithmeticTypes::Vec3d & aPoint, int w, int h){
+	int scale = MAX(w,h);
+	double x = (aPoint.x * .1 / scale) - .05;
+	/* in Java Swing (0,0) is the top-left corner, whereas in openGL and HAPI * 
+	 * (0,0) is the bottom-left corner. Therefore the y must be converted     */
+	aPoint.y = h - aPoint.y;
+	double y = (aPoint.y * .1 / scale) - .05;
+	
+	aPoint.x = x;
+	aPoint.y = y;
+	return aPoint;
+}
+
+H3DUtil::ArithmeticTypes::Vec3d & hapticToScreenSpace(H3DUtil::ArithmeticTypes::Vec3d & aPoint, int w, int h){
+	int scale = MAX(w,h);
+	double x = (aPoint.x + .05) * scale * 10;
+	double y = (aPoint.y + .05) * scale * 10;
+
+	aPoint.x = x;
+	/* in Java Swing (0,0) is the top-left corner, whereas in openGL and HAPI * 
+	 * (0,0) is the bottom-left corner. Therefore the y must be converted     */
+	aPoint.y = h - y;
+	return aPoint;
+}
+
+
+double pointsDistance(const H3DUtil::ArithmeticTypes::Vec3d & p, const H3DUtil::ArithmeticTypes::Vec3d & q){
+	/* 2D vectors are assumed */
+	return sqrt( (p.x - q.x)*(p.x - q.x)  +  (p.y - q.y)*(p.y - q.y) ) ;
+}
+
+H3DUtil::ArithmeticTypes::Vec3d midPoint(const H3DUtil::ArithmeticTypes::Vec3d & start, const H3DUtil::ArithmeticTypes::Vec3d & end){
+	/* 2D vectors are assumed */
+	double dx = abs((start.x - end.x)/2); 
+	double dy = abs((start.y - end.y)/2);
+	return H3DUtil::ArithmeticTypes::Vec3d( MIN(start.x,end.x)+dx , MIN(start.y,end.y)+dy , 0);
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/Falcon/utils.h	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,45 @@
+/*  
+ CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
+  
+ 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/>.
+*/  
+
+#pragma once
+
+#include <jni.h>
+#include <iostream>
+#include <H3DUtil/Vec3d.h>
+#include <math.h>
+
+/* check for Java exception */
+void checkExceptions(JNIEnv *env, char* what);
+
+/* exit the program printing a message on the sttandard error */
+void stopExecution(char* msg);
+
+/* transforms the coordinates of aPoint from the screen space (the system used in the graphic diagram) *
+ * to the haptic space which is used by the Falcon and goes from about -0.05 to 0.05 on each axis      */
+H3DUtil::ArithmeticTypes::Vec3d & screenToHapticSpace(H3DUtil::ArithmeticTypes::Vec3d & aPoint, int w, int h);
+
+/* does the inverse of screenToHapticSpace */
+H3DUtil::ArithmeticTypes::Vec3d & hapticToScreenSpace(H3DUtil::ArithmeticTypes::Vec3d & aPoint, int w, int h);
+
+/* Calculates the distance between points on a plane whose normal is the Z-axis. This  *
+ * means that the z coordinate in not taken into account for the distance calculation  */
+double pointsDistance(const H3DUtil::ArithmeticTypes::Vec3d & p, const H3DUtil::ArithmeticTypes::Vec3d & q);
+
+/* find the midpoint of a segment foing from start to end. The z coordinate of start and end is not taken into account */
+H3DUtil::ArithmeticTypes::Vec3d midPoint(const H3DUtil::ArithmeticTypes::Vec3d & start, const H3DUtil::ArithmeticTypes::Vec3d & end);
--- a/native/PhantomOmni/CollectionsManager.h	Tue May 29 15:32:19 2012 +0100
+++ b/native/PhantomOmni/CollectionsManager.h	Tue Jul 10 22:39:37 2012 +0100
@@ -134,7 +134,7 @@
 	bool isEdge(const jint id) const throw(int);
 	bool isNode(const jint id) const throw(int);
 	void init(void);
-	int getScreenHeight() const { return screenHeight;}
-	void setScreenHeight(const int sh) { screenHeight = sh; }
+	inline int getScreenHeight() const { return screenHeight;}
+	inline void setScreenHeight(const int sh) { screenHeight = sh; }
 	
 };
--- a/native/PhantomOmni/HapticManager.cpp	Tue May 29 15:32:19 2012 +0100
+++ b/native/PhantomOmni/HapticManager.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -447,7 +447,7 @@
 		}
 	}
 
-	/* draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift.   *
+	/*  draw lines. When a node is moved, edges are not drawn for the very first haptic frame after the shift.   *
 	 *  This is to address the behaviour of the device which, after a n ode is moved cannot see the styilus     *
 	 *  touching the edge. As a consequence of that in hlMotionCB() when moving away from a node along an edge  *
 	 *  no edge name will be spoken and furthermore the pick up botton won't work as the device thinks it's not *  
--- a/native/PhantomOmni/Haptics.vcproj	Tue May 29 15:32:19 2012 +0100
+++ b/native/PhantomOmni/Haptics.vcproj	Tue Jul 10 22:39:37 2012 +0100
@@ -2,7 +2,7 @@
 <VisualStudioProject
 	ProjectType="Visual C++"
 	Version="9.00"
-	Name="Haptics"
+	Name="OmniHaptics"
 	ProjectGUID="{3FAB66F5-BA54-470F-9D4B-3E73E58A76BC}"
 	RootNamespace="Haptics"
 	TargetFrameworkVersion="131072"
@@ -49,7 +49,7 @@
 				Name="VCCLCompilerTool"
 				Optimization="2"
 				InlineFunctionExpansion="1"
-				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_25\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_25\include\win32&quot;;include;&quot;$(3DTOUCH_BASE)\include&quot;;&quot;$(3DTOUCH_BASE)\utilities\include&quot;"
+				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_25\include&quot;;&quot;C:\Program Files\Java\jdk1.6.0_25\include\win32&quot;;&quot;$(3DTOUCH_BASE)\include&quot;;&quot;$(3DTOUCH_BASE)\utilities\include&quot;"
 				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
 				StringPooling="true"
 				RuntimeLibrary="2"
@@ -137,7 +137,7 @@
 			<Tool
 				Name="VCCLCompilerTool"
 				Optimization="0"
-				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_24\include\win32&quot;;&quot;C:\Program Files\Java\jdk1.6.0_24\include&quot;;include;&quot;$(3DTOUCH_BASE)\include&quot;;&quot;$(3DTOUCH_BASE)\utilities\include&quot;"
+				AdditionalIncludeDirectories="&quot;C:\Program Files\Java\jdk1.6.0_25\include\win32&quot;;&quot;C:\Program Files\Java\jdk1.6.0_25\include&quot;;&quot;$(3DTOUCH_BASE)\include&quot;;&quot;$(3DTOUCH_BASE)\utilities\include&quot;"
 				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
 				BasicRuntimeChecks="3"
 				RuntimeLibrary="3"
@@ -410,7 +410,7 @@
 				>
 			</File>
 			<File
-				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_Haptics.cpp"
+				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.cpp"
 				>
 				<FileConfiguration
 					Name="Release|Win32"
@@ -485,7 +485,7 @@
 				>
 			</File>
 			<File
-				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_Haptics.h"
+				RelativePath=".\uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.h"
 				>
 			</File>
 			<File
--- a/native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_Haptics.cpp	Tue May 29 15:32:19 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,518 +0,0 @@
-#include "uk_ac_qmul_eecs_ccmi_haptics_Haptics.h"
-#include "stdafx.h"
-#include "GraphicManager.h"
-#include "HapticManager.h"
-#include "HapticException.h"
-#include <stdlib.h>
-#include <math.h>
-#include <assert.h>
-#include <setjmp.h>
-#include "utils.h"
-
-#define CURSOR_SIZE_PIXELS 20
-
-enum {JMP_OK =0, JMP_EXIT };
-
-/**************************
- Function prototypes. 
- ***************************/
-/* callbacks */
-void displayCallback(void);
-void reshapeCallback(int width, int height);
-void idleCallback(void);   
-void exitProcedure(void);
-void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y,const jdouble startX, const jdouble startY);
-
-void drawCursor();
-void updateWorkspace();
-
-void initJniVariables(void);
-/*************************
- global variables 
- **************************/
-GraphicManager *gManager;
-HapticManager *hManager;
-CollectionsManager *cManager;
-JNIEnv *env;
-jobject *lock;
-int width;
-int height;
-jmp_buf jmpenv;	
-int jmpval = 0;
-/* jni variables */
-jclass hapticClass;
-jclass hapticListenerClass;
-jfieldID shutdownfieldId;
-jfieldID newHapticIdfieldId;
-jfieldID dumpHapticIdfieldId;
-jfieldID currentHapticIdfieldId;
-jfieldID hapticInitFailedfieldId;
-jfieldID attractTofieldId;
-jfieldID attractToHapticIdFieldId;
-jfieldID pickUpfieldId;
-jfieldID pickUpHapticIdFieldId;
-jfieldID hapticListenerFieldId;
-jfieldID cmdFieldId; // belongs to the haptic listener
-jfieldID diagramElementFieldId; // belongs to the haptic listener
-jfieldID xFieldId; // belongs to the haptic listener
-jfieldID yFieldId; // belongs to the haptic listener
-jfieldID startXFieldId; // belongs to the haptic listener
-jfieldID startYFieldId; // belongs to the haptic listener
-jobject hapticListener;
-jmethodID notifyMethodId;
-jmethodID notifyListenerMethodId;
-jmethodID waitMethodId;
-
-/*******************************************************************************
- Initializes GLUT for displaying a simple haptic scene.
-*******************************************************************************/
-JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_init
-  (JNIEnv *environment, jobject obj, jint w, jint h){
-	env = environment;
-	lock = &obj;
-	/* fake main argv and argc as this is a dll */
-	char *argv[1] = {"OmniHaptics"}; 
-	int argc = 1;
-
-	initJniVariables();
-
-	/* glut initialization */
-    glutInit(&argc, argv);
-    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
-    glutInitWindowSize(w, h);
-    glutCreateWindow("CCmI Diagram Haptics");
-
-    /* glut callbacks */
-    glutDisplayFunc(displayCallback);
-    glutReshapeFunc(reshapeCallback);
-    glutIdleFunc(idleCallback);
-	
-	cManager = new CollectionsManager(env,lock);
-	hManager = new HapticManager(cManager,hapticCommandCB);
-	gManager = new GraphicManager(cManager,hManager);
-	cManager->init();
-	gManager->init();    
-		
-	// try to initialize the haptic, tell the java thread about the success/failure. The endeavour
-	// takes the lock on the haptics java object in order to access the nodes and edges concurrently 
-	if(env->MonitorEnter(*lock) != JNI_OK){
-		stopExecution("Could not allocate memory for haptic thread monitor");
-	}
-	checkExceptions(env, "Could not enter monitor on Haptics");
-
-	bool mustSayGoodbye = false;
-	try{ 
-		hManager->init();
-	}catch (HapticException e){
-		env->SetBooleanField(*lock,hapticInitFailedfieldId,JNI_TRUE);
-		mustSayGoodbye = true;
-	}
-	
-	if(mustSayGoodbye)
-		std::cout << "Failed to initialize haptic device" << std::endl;
-	else
-		std::cout << "Haptic device successfully initialized" << std::endl;
-
-	// notify the other thread
-	env->CallVoidMethod(*lock,notifyMethodId);
-	checkExceptions(env,"Could not call notify() on Haptics");
-	
-	//release the lock
-	if(env->MonitorExit(*lock) != JNI_OK){
-		std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
-		exit(-1);
-	}
-	
-	if(mustSayGoodbye)
-		/* initialization failed: return */
-		return -1;
-
-	/* use setjmp to be able to jump off the glutMainLoop when the user shuts the program down */
-	jmpval = setjmp(jmpenv);
-
-	/* star the loop*/
-	if(jmpval == JMP_OK){
-		glutMainLoop();
-	}else{
-		exitProcedure();
-	}
-	return 0;
-}
-
-/*******************************************************************************
- GLUT callback for redrawing the view.
-*******************************************************************************/
-void displayCallback(){ 
-
-	// takes the lock on the haptics java obejct in order to access the nodes and edges concurrently 
-	if(env->MonitorEnter(*lock) != JNI_OK){
-		stopExecution("Could not allocate memory for haptic thread monitor");
-	}
-	checkExceptions(env,"Could not enter monitor on Haptics");
-
-	// check if there is a shutdown request 
-	if( env->GetBooleanField(*lock,shutdownfieldId) == JNI_TRUE ){
-		// notify the other thread that this thread is about to die
-		env->CallVoidMethod(*lock,notifyMethodId);
-		checkExceptions(env, "Could not call notify() on Haptics");
-		// release the lock 
-		if(env->MonitorExit(*lock) != JNI_OK){
-			std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
-		}
-		longjmp(jmpenv,JMP_EXIT);
-	}
-
-	// check if the user asked to be attracted to a node
-	jboolean attractTo = env->GetBooleanField(*lock,attractTofieldId);
-	jint attractToDiagramId = 0;
-	if(attractTo == JNI_TRUE){
-		env->SetBooleanField(*lock,attractTofieldId,JNI_FALSE);
-		jint attractToHapticId = env->GetIntField(*lock,attractToHapticIdFieldId);
-		if(cManager->isNode(attractToHapticId)){
-			attractToDiagramId = cManager->getNodeDataFromID(attractToHapticId).diagramId;
-		}else{
-			attractToDiagramId = cManager->getEdgeDataFromID(attractToHapticId).diagramId;
-		}
-		hManager->setAttractTo(attractToHapticId);
-	} 
-	// check if the user picked up a node
-	jboolean pickUp = env->GetBooleanField(*lock,pickUpfieldId);
-	jint pickUpDiagramId = 0;
-	if(pickUp == JNI_TRUE){
-		env->SetBooleanField(*lock,pickUpfieldId,JNI_FALSE);
-		jint pickUpHapticId = env->GetIntField(*lock,pickUpHapticIdFieldId);
-		if(cManager->isNode(pickUpHapticId)){
-			pickUpDiagramId = cManager->getNodeDataFromID(pickUpHapticId).diagramId;
-		}else{
-			pickUpDiagramId = cManager->getEdgeDataFromID(pickUpHapticId).diagramId;
-		}
-		hManager->pickUp(pickUpDiagramId);
-	}
-
-	// draw the scene graphically and haptically 
-	hManager->draw(); 
-	gManager->draw();
-
-	// check whether the java thread needs to either create or dump an haptic id 
-	jboolean needsNewHapticId = env->GetBooleanField(*lock,newHapticIdfieldId);
-	if(needsNewHapticId == JNI_TRUE){	
-		// set int currentHapticId of class Haptics with a new generated id
-		jint newHapticid = hlGenShapes(1);
-		env->SetIntField(*lock, currentHapticIdfieldId, newHapticid);
-		//set the boolean field to false as the other thread now has an id
-		env->SetBooleanField(*lock, newHapticIdfieldId, JNI_FALSE);
-		//notify the other thread	
-		env->CallVoidMethod(*lock,notifyMethodId);
-		checkExceptions(env, "Could not call notify() on Haptics");
-	}
-	jboolean needsDumpOldHapticId = env->GetBooleanField(*lock, dumpHapticIdfieldId);
-	if(needsDumpOldHapticId == JNI_TRUE){
-		// get the id of the deleted element from the other thread
-		jint oldHapticId = env->GetIntField(*lock,currentHapticIdfieldId);
-		// free the old haptic id 
-		hlDeleteShapes(oldHapticId,1);
-		// set the boolean field as the id has been cleaned up
-		env->SetBooleanField(*lock,dumpHapticIdfieldId,JNI_FALSE);
-		// notify the other thread
-		env->CallVoidMethod(*lock,notifyMethodId);
-		checkExceptions(env, "Could not call notify() on Haptics");
-	}
-	
-	/* release lock */
-	if(env->MonitorExit(*lock) != JNI_OK){
-		stopExecution("Could not release memory for haptic thread monitor");
-	}
-
-	/* it's important that this call be outside the monitors, else a deadlock occurs */
-	// Call any event callbacks that have been triggered.
-	if(attractTo == JNI_TRUE){
-		if(hManager->wasAlreadyTouchingAttractingElement()){
-			hapticCommandCB('t',attractToDiagramId,0,0,0,0);
-		}
-	}
-
-	hlCheckEvents();
-	HapticManager::ClickStatus click = hManager->getButton1Status();
-	if(click == HapticManager::ONE_CLICK){
-		hManager->doButton1Click(false);
-	}else if(click == HapticManager::TWO_CLICK){
-		hManager->doButton1Click(true);
-	}
-
-    glutSwapBuffers();
-}
-/*******************************************************************************
- GLUT callback for reshaping the window.  This is the main place where the 
- viewing and workspace transforms get initialized.
-*******************************************************************************/
-void reshapeCallback(int w, int h){
-    static const double kPI = 3.1415926535897932384626433832795;
-    static const double kFovY = 40;
-
-    static const double nearDist = 1.0 / tan((kFovY / 2.0) * kPI / 180.0 /* radiants for 1 degree */);
-	static const double farDist = nearDist + 2.0;
-	double aspect;
-	width = w;
-	height = h;
-
-	cManager->setScreenHeight(h);
-    glViewport(0, 0, width, height);
-    // Compute the viewing parameters based on a fixed fov and viewing
-    // a canonical box centered at the origin.
-    aspect = (double)  width/height ;
-
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    gluPerspective(kFovY, aspect,nearDist, farDist);
-	// Place the camera down the Z axis looking at the origin.
-	glMatrixMode(GL_MODELVIEW);
-     glLoadIdentity();            
-    gluLookAt(0, 0, nearDist + 1.0,
-              0, 0, 0,
-             0, 1, 0);
-
-	hduVector3Dd origin;
-	fromScreen(hduVector3Dd(0, 0, 0),origin);
-	glLoadIdentity(); 
-	gluLookAt(-origin[0],origin[1], nearDist + 1.0,
-              -origin[0],origin[1], 0,
-               0, 1, 0);
-
-    hduVector3Dd end;
-	fromScreen(hduVector3Dd(w, h, 0),end);
-	
-	//glTranslatef(end[0]-origin[0],-(end[1]-origin[1]),0);
-	
-	updateWorkspace();
-}
-
-
-/*******************************************************************************
- Use the current OpenGL viewing transforms to initialize a transform for the
- haptic device workspace so that it's properly mapped to world coordinates.
-*******************************************************************************/
-void updateWorkspace(){
-    GLdouble modelview[16];
-    GLdouble projection[16];
-    GLint viewport[4];
-
-    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
-    glGetDoublev(GL_PROJECTION_MATRIX, projection);
-    glGetIntegerv(GL_VIEWPORT, viewport);
-	
-	hlMatrixMode(HL_TOUCHWORKSPACE);
-    hlLoadIdentity();
-    
-    // Fit haptic workspace to view volume.
-    hluFitWorkspace(projection);
-
-    // Compute cursor scale.
-	gManager->gCursorScale = hluScreenToModelScale(modelview, projection, viewport);
-	gManager->gCursorScale *= CURSOR_SIZE_PIXELS;
-
-	hduVector3Dd p0, p1;
-    bool bNoError;
-
-    bNoError = fromScreen(hduVector3Dd(0, 0, 0), p0);
-    assert(bNoError);
-    
-    bNoError = fromScreen(hduVector3Dd(1, 1, 0), p1);
-    assert(bNoError);
-
-    double m_windowTworldScale = (p1 - p0).magnitude() / sqrt(2.0);
-	gManager->gWorldScale = m_windowTworldScale;
-}
-/*******************************************************************************
- GLUT callback for idle state.  Use this as an opportunity to request a redraw.
- Checks for HLAPI errors that have occurred since the last idle check.
-*******************************************************************************/
-void idleCallback(){
-    HLerror error;
-
-    while (HL_ERROR(error = hlGetError())){
-		std::cerr <<  "HL Error: " << error.errorCode << std::endl <<error.errorInfo << std::endl;
-        
-        if (error.errorCode == HL_DEVICE_ERROR){
-            hduPrintError(stderr, &error.errorInfo, "Device error\n");
-			std::cout << "sending error message to haptic listener" << std::endl;
-			hapticCommandCB('e',0,0,0,0,0); 
-        }
-    }
-    glutPostRedisplay();
-}
-
-/*******************************************************************************
- This handler is called when the application is exiting.  Deallocates any state 
- and cleans up.
-*******************************************************************************/
-void exitProcedure(){
-    
-    // Free up the haptic rendering context.
-    hlMakeCurrent(NULL);
-	
-	if (HapticManager::ghHLRC != NULL){
-        hlDeleteContext(HapticManager::ghHLRC);
-    }
-
-    // Free up the haptic device.
-    if (HapticManager::ghHD != HD_INVALID_HANDLE){
-        hdDisableDevice(HapticManager::ghHD);
-    }
-	std::cout << "freeing haptic resources" << std::endl;
-}
-
-/* initialize all the variable needed for the jni access to the Haptics class from the openGL thread*/
-void initJniVariables(void){
-	/* --- CLASSES --- */
-	//this class
-	hapticClass = env->GetObjectClass(*lock);
-	if(hapticClass == NULL){
-		stopExecution("Could not find the Haptics class");
-	}
-	// the haptic listener, member of this class
-	hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/HapticListener;");
-	if(hapticListenerClass == NULL){
-		stopExecution("Could not find the haptic listener class");
-	}
-
-	/* --- FIELD IDS --- */
-	// boolean set by the java thread when an element is added and it needs a new id from the haptic library
-	newHapticIdfieldId = env->GetFieldID(hapticClass,"newHapticId", "Z");
-	if(newHapticIdfieldId == NULL){
-		stopExecution("failed to find the newHapticId field id");
-	}
-
-	// boolean set by the java thread when an element is added and it needs a new id from the haptic library
-	dumpHapticIdfieldId = env->GetFieldID(hapticClass,"dumpHapticId", "Z");
-	if(dumpHapticIdfieldId == NULL){
-		stopExecution("failed to find the dumpHapticId field id");
-	}
-
-	// boolean set to this thread to notify the java thread the unsuccessful initialization of haptic device
-	hapticInitFailedfieldId = env->GetFieldID(hapticClass,"hapticInitFailed", "Z");
-	if(hapticInitFailedfieldId == NULL){
-		stopExecution("failed to find the hapticInitFailedfieldId field id");
-	}
-
-	// boolean set by the java thread to notify this thread the program has been shut down 
-	shutdownfieldId = env->GetFieldID(hapticClass, "shutdown", "Z");
-	if(shutdownfieldId == NULL){
-		stopExecution("failed to find the shutdownfieldId field id");
-	}
-
-	// boolean set by the java thread when the user asks to sna
-	attractTofieldId = env->GetFieldID(hapticClass, "attractTo" , "Z");
-	if(shutdownfieldId == NULL){
-		stopExecution("failed to find the attractTo field id");
-	}
-
-	attractToHapticIdFieldId = env->GetFieldID(hapticClass, "attractToHapticId", "I");
-	if(attractToHapticIdFieldId == NULL){
-		stopExecution("failed to find the attractToHapticId field id");
-	}
-
-	pickUpfieldId = env->GetFieldID(hapticClass,"pickUp","Z");
-	if(pickUpfieldId == NULL){
-		stopExecution("failed to find the pickUp field id");
-	}
-
-	pickUpHapticIdFieldId = env->GetFieldID(hapticClass,"pickUpHapticId","I");
-	if(pickUpHapticIdFieldId == NULL){
-		stopExecution("failed to find pickUpHapticId field id");
-	}
-
-	hapticListenerFieldId = env->GetFieldID(hapticClass, "hapticListener", "Luk/ac/qmul/eecs/ccmi/haptics/HapticListener;");
-	if(hapticListenerFieldId == NULL){
-		stopExecution("failed to find the hapticListener field id");
-	}
-
-	// variable to exchange values between threads
-	currentHapticIdfieldId = env->GetFieldID(hapticClass,"currentHapticId","I");
-	if(currentHapticIdfieldId == NULL){
-		stopExecution("failed to find the currentHapticId field");
-	}
-
-	cmdFieldId = env->GetFieldID(hapticListenerClass,"cmd", "C");
-	if(cmdFieldId == NULL){
-		stopExecution("failed to find the cmd field id of the hapticListener class");
-	}
-
-	diagramElementFieldId = env->GetFieldID(hapticListenerClass, "diagramElementID", "I");
-	if(diagramElementFieldId == NULL){
-		stopExecution("failed to find the diagramElement field id of the hapticListener class");
-	}
-
-	xFieldId = env->GetFieldID(hapticListenerClass, "x", "D");
-	if(xFieldId == NULL){
-		stopExecution("failed to find the x field id of the hapticListener class");
-	}
-
-	yFieldId = env->GetFieldID(hapticListenerClass, "y", "D");
-	if(yFieldId == NULL){
-		stopExecution("failed to find the y field id of the hapticListener class");
-	}
-
-	startXFieldId = env->GetFieldID(hapticListenerClass, "startX", "D");
-	if(startXFieldId == NULL){
-		stopExecution("failed to find the x field id of the hapticListener class");
-	}
-
-	startYFieldId = env->GetFieldID(hapticListenerClass, "startY", "D");
-	if(startYFieldId == NULL){
-		stopExecution("failed to find the y field id of the hapticListener class");
-	}
-
-	hapticListener = env->GetObjectField(*lock,hapticListenerFieldId);
-	/* --- METHOD IDs --- */
-	// notify method
-	notifyMethodId = env->GetMethodID(hapticClass,"notify","()V");
-	if(notifyMethodId == NULL){
-		stopExecution("failed to find the notify method id");
-	}
-
-	notifyListenerMethodId = env->GetMethodID(hapticListenerClass,"notify","()V");
-	if(notifyListenerMethodId == NULL){
-		stopExecution("failed to find the notify method id");
-	}
-
-	waitMethodId = env->GetMethodID(hapticListenerClass,"wait","()V");
-	if(waitMethodId == NULL){
-		stopExecution("failed to find the wait method id");
-	}
-}
-
-void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y, const jdouble startX, const jdouble startY){
-	/* the haptic listener java thread is waiting for commands
-	   first set the variable, the Haptic Listener java thread will read after being notified,
-	   then notify and get it awake. Thus wait for the java thread to notify that the command 
-	   has been accomplished. This is done as otherwise some commands might be neglected. as if the thread 
-	   scheduler decides to execute twice this routine without executing the java thread in the middle then 
-	   the former command gets overwritten by the latter. 
-	   Note the monitor is hapticListener and not haptics as for the draw function. 
-	   When in this routine, this thread does not hold the lock on haptics as if a command results in changing
-	   the elements collections (e.g. moveNode) all those methods are synchronized and require to acquire the lock on haptics.
-	   Since this thread would wait for the command to be executed by the java thread, which in turns would wait for this
-	   thread to release the lock on haptics, that would result in a deadlock. 
-	 */
-	/* now wake up the haptic listener */
-	if(env->MonitorEnter(hapticListener) != JNI_OK){
-		stopExecution("Could not allocate memory for haptic listener thread monitor");
-	}
-	checkExceptions(env,"Could not enter monitor on the haptic listener");
-	env->SetCharField(hapticListener,cmdFieldId,cmd);
-	env->SetIntField(hapticListener,diagramElementFieldId,ID);
-	env->SetDoubleField(hapticListener,xFieldId,x);
-	env->SetDoubleField(hapticListener,yFieldId,y);
-	env->SetDoubleField(hapticListener,startXFieldId,startX);
-	env->SetDoubleField(hapticListener,startYFieldId,startY);
-	// wake the java thread up to execute the command
-	env->CallVoidMethod(hapticListener,notifyListenerMethodId);
-	checkExceptions(env, "Could not call notify() on HapticListener");
-	/* wait for the commands to be executed. Here is actually where the monitor is
-	 * freed and the java thread starts to execute the command, having been notified */ 
-	env->CallVoidMethod(hapticListener,waitMethodId);
-	checkExceptions(env, "Could not call wait() on HapticListener");
-	if(env->MonitorExit(hapticListener) != JNI_OK){
-		stopExecution("Could not release memory for haptic listener thread monitor");
-	}
-}
\ No newline at end of file
--- a/native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_Haptics.h	Tue May 29 15:32:19 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics */
-
-#ifndef _Included_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
-#define _Included_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
-#ifdef __cplusplus
-extern "C" {
-#endif
-#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MIN_PRIORITY
-#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MIN_PRIORITY 1L
-#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_NORM_PRIORITY
-#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_NORM_PRIORITY 5L
-#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MAX_PRIORITY
-#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MAX_PRIORITY 10L
-/*
- * Class:     uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
- * Method:    init
- * Signature: (II)I
- */
-JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_init
-  (JNIEnv *, jobject, jint, jint);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.cpp	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,524 @@
+#include "uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.h"
+#include "stdafx.h"
+#include "GraphicManager.h"
+#include "HapticManager.h"
+#include "HapticException.h"
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <setjmp.h>
+#include "utils.h"
+
+#define CURSOR_SIZE_PIXELS 20
+
+enum {JMP_OK =0, JMP_EXIT };
+
+/**************************
+ Function prototypes. 
+ ***************************/
+/* callbacks */
+void displayCallback(void);
+void reshapeCallback(int width, int height);
+void idleCallback(void);   
+void exitProcedure(void);
+void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y,const jdouble startX, const jdouble startY);
+
+void drawCursor();
+void updateWorkspace();
+
+void initJniVariables(void);
+/*************************
+ global variables 
+ **************************/
+static GraphicManager *gManager;
+static HapticManager *hManager;
+static CollectionsManager *cManager;
+JNIEnv *env;
+jobject *lock;
+static int width;
+static int height;
+static jmp_buf jmpenv;	
+static int jmpval = 0;
+/* jni variables */
+jclass hapticClass;
+jclass hapticListenerClass;
+jfieldID shutdownfieldId;
+jfieldID newHapticIdfieldId;
+jfieldID dumpHapticIdfieldId;
+jfieldID currentHapticIdfieldId;
+jfieldID hapticInitFailedfieldId;
+jfieldID attractTofieldId;
+jfieldID attractToHapticIdFieldId;
+jfieldID pickUpfieldId;
+jfieldID pickUpHapticIdFieldId;
+jfieldID hapticListenerFieldId;
+jfieldID cmdFieldId; // belongs to the haptic listener
+jfieldID diagramElementFieldId; // belongs to the haptic listener
+jfieldID xFieldId; // belongs to the haptic listener
+jfieldID yFieldId; // belongs to the haptic listener
+jfieldID startXFieldId; // belongs to the haptic listener
+jfieldID startYFieldId; // belongs to the haptic listener
+jobject hapticListener;
+jmethodID notifyMethodId;
+jmethodID notifyListenerMethodId;
+jmethodID waitMethodId;
+
+/*******************************************************************************
+ Initializes GLUT for displaying a simple haptic scene.
+*******************************************************************************/
+JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_initOmni
+  (JNIEnv *environment, jobject obj, jint w, jint h){
+	env = environment;
+	lock = &obj;
+	/* fake main argv and argc as this is a dll */
+	char *argv[1] = {"OmniHaptics"}; 
+	int argc = 1;
+
+	initJniVariables();
+
+	/* glut initialization */
+    glutInit(&argc, argv);
+    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
+    glutInitWindowSize(w, h);
+    glutCreateWindow("CCmI Diagram Haptics");
+
+    /* glut callbacks */
+    glutDisplayFunc(displayCallback);
+    glutReshapeFunc(reshapeCallback);
+    glutIdleFunc(idleCallback);
+	
+	cManager = new CollectionsManager(env,lock);
+	hManager = new HapticManager(cManager,hapticCommandCB);
+	gManager = new GraphicManager(cManager,hManager);
+	cManager->init();
+	gManager->init();    
+		
+	// try to initialize the haptic, tell the java thread about the success/failure. The endeavour
+	// takes the lock on the haptics java object in order to access the nodes and edges concurrently 
+	if(env->MonitorEnter(*lock) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic thread monitor");
+	}
+	checkExceptions(env, "Could not enter monitor on Haptics");
+
+	bool mustSayGoodbye = false;
+	try{ 
+		hManager->init();
+	}catch (HapticException e){
+		/* set the field in the java class to comunicate the main thread (waiting   *
+		 * for the initialization to complete) that the initialization failed       */
+		env->SetBooleanField(*lock,hapticInitFailedfieldId,JNI_TRUE);
+		mustSayGoodbye = true;
+	}
+	
+	if(mustSayGoodbye)
+		std::cout << "Failed to initialize Omni Haptic device" << std::endl;
+	else
+		std::cout << "Omni Haptic device successfully initialized" << std::endl;
+
+	// notify the other thread
+	env->CallVoidMethod(*lock,notifyMethodId);
+	checkExceptions(env,"Could not call notify() on Haptics");
+	
+	//release the lock
+	if(env->MonitorExit(*lock) != JNI_OK){
+		std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
+		exit(-1);
+	}
+	
+	if(mustSayGoodbye)
+		/* initialization failed: return */
+		return -1;
+
+	/* use setjmp to be able to jump off the glutMainLoop when the user shuts the program down */
+	jmpval = setjmp(jmpenv);
+
+	/* star the loop*/
+	if(jmpval == JMP_OK){
+		glutMainLoop();
+	}
+	
+	exitProcedure();
+	
+	return 0;
+}
+
+/*******************************************************************************
+ GLUT callback for redrawing the view.
+*******************************************************************************/
+void displayCallback(){ 
+
+	// takes the lock on the haptics java obejct in order to access the nodes and edges concurrently 
+	if(env->MonitorEnter(*lock) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic thread monitor");
+	}
+	checkExceptions(env,"Could not enter monitor on Haptics");
+
+	// check if there is a shutdown request 
+	if( env->GetBooleanField(*lock,shutdownfieldId) == JNI_TRUE ){
+		// notify the other thread that this thread is about to die
+		env->CallVoidMethod(*lock,notifyMethodId);
+		checkExceptions(env, "Could not call notify() on Haptics");
+		// release the lock 
+		if(env->MonitorExit(*lock) != JNI_OK){
+			std::cerr << "Could not release memory for haptic thread monitor" << std::endl;
+		}
+		longjmp(jmpenv,JMP_EXIT);
+	}
+
+	// check if the user asked to be attracted to a node
+	jboolean attractTo = env->GetBooleanField(*lock,attractTofieldId);
+	jint attractToDiagramId = 0;
+	if(attractTo == JNI_TRUE){
+		env->SetBooleanField(*lock,attractTofieldId,JNI_FALSE);
+		jint attractToHapticId = env->GetIntField(*lock,attractToHapticIdFieldId);
+		if(cManager->isNode(attractToHapticId)){
+			attractToDiagramId = cManager->getNodeDataFromID(attractToHapticId).diagramId;
+		}else{
+			attractToDiagramId = cManager->getEdgeDataFromID(attractToHapticId).diagramId;
+		}
+		hManager->setAttractTo(attractToHapticId);
+	} 
+	// check if the user picked up a node
+	jboolean pickUp = env->GetBooleanField(*lock,pickUpfieldId);
+	jint pickUpDiagramId = 0;
+	if(pickUp == JNI_TRUE){
+		env->SetBooleanField(*lock,pickUpfieldId,JNI_FALSE);
+		jint pickUpHapticId = env->GetIntField(*lock,pickUpHapticIdFieldId);
+		if(cManager->isNode(pickUpHapticId)){
+			pickUpDiagramId = cManager->getNodeDataFromID(pickUpHapticId).diagramId;
+		}else{
+			pickUpDiagramId = cManager->getEdgeDataFromID(pickUpHapticId).diagramId;
+		}
+		hManager->pickUp(pickUpDiagramId);
+	}
+
+	// draw the scene graphically and haptically 
+	hManager->draw(); 
+	gManager->draw();
+
+	// check whether the java thread needs to either create or dump an haptic id 
+	jboolean needsNewHapticId = env->GetBooleanField(*lock,newHapticIdfieldId);
+	if(needsNewHapticId == JNI_TRUE){	
+		// set int currentHapticId of class Haptics with a new generated id
+		jint newHapticid = hlGenShapes(1);
+		env->SetIntField(*lock, currentHapticIdfieldId, newHapticid);
+		//set the boolean field to false as the other thread now has an id
+		env->SetBooleanField(*lock, newHapticIdfieldId, JNI_FALSE);
+		//notify the other thread	
+		env->CallVoidMethod(*lock,notifyMethodId);
+		checkExceptions(env, "Could not call notify() on Haptics");
+	}
+	jboolean needsDumpOldHapticId = env->GetBooleanField(*lock, dumpHapticIdfieldId);
+	if(needsDumpOldHapticId == JNI_TRUE){
+		// get the id of the deleted element from the other thread
+		jint oldHapticId = env->GetIntField(*lock,currentHapticIdfieldId);
+		// free the old haptic id 
+		hlDeleteShapes(oldHapticId,1);
+		// set the boolean field as the id has been cleaned up
+		env->SetBooleanField(*lock,dumpHapticIdfieldId,JNI_FALSE);
+		// notify the other thread
+		env->CallVoidMethod(*lock,notifyMethodId);
+		checkExceptions(env, "Could not call notify() on Haptics");
+	}
+	
+	/* release lock */
+	if(env->MonitorExit(*lock) != JNI_OK){
+		stopExecution("Could not release memory for haptic thread monitor");
+	}
+
+	/* it's important that this call be outside the monitors, else a deadlock occurs */
+	// Call any event callbacks that have been triggered.
+	if(attractTo == JNI_TRUE){
+		if(hManager->wasAlreadyTouchingAttractingElement()){
+			hapticCommandCB('t',attractToDiagramId,0,0,0,0);
+		}
+	}
+
+	hlCheckEvents();
+	/* button one is not based on events only , but also on time lapse as the single click is effective   *
+	 * after an amount of time (if another click is not issued). Therefore we need to continuosly for the *
+	 * button status, hence the work must be done here and not in the click callbacks                     */
+	HapticManager::ClickStatus click = hManager->getButton1Status();
+	if(click == HapticManager::ONE_CLICK){
+		hManager->doButton1Click(false);
+	}else if(click == HapticManager::TWO_CLICK){
+		hManager->doButton1Click(true);
+	}
+
+    glutSwapBuffers();
+}
+/*******************************************************************************
+ GLUT callback for reshaping the window.  This is the main place where the 
+ viewing and workspace transforms get initialized.
+*******************************************************************************/
+void reshapeCallback(int w, int h){
+    static const double kPI = 3.1415926535897932384626433832795;
+    static const double kFovY = 40;
+
+    static const double nearDist = 1.0 / tan((kFovY / 2.0) * kPI / 180.0 /* radiants for 1 degree */);
+	static const double farDist = nearDist + 2.0;
+	double aspect;
+	width = w;
+	height = h;
+
+	cManager->setScreenHeight(h);
+    glViewport(0, 0, width, height);
+    // Compute the viewing parameters based on a fixed fov and viewing
+    // a canonical box centered at the origin.
+    aspect = (double)  width/height ;
+
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    gluPerspective(kFovY, aspect,nearDist, farDist);
+	// Place the camera down the Z axis looking at the origin.
+	glMatrixMode(GL_MODELVIEW);
+     glLoadIdentity();            
+    gluLookAt(0, 0, nearDist + 1.0,
+              0, 0, 0,
+             0, 1, 0);
+
+	hduVector3Dd origin;
+	fromScreen(hduVector3Dd(0, 0, 0),origin);
+	glLoadIdentity(); 
+	gluLookAt(-origin[0],origin[1], nearDist + 1.0,
+              -origin[0],origin[1], 0,
+               0, 1, 0);
+
+    hduVector3Dd end;
+	fromScreen(hduVector3Dd(w, h, 0),end);
+	
+	//glTranslatef(end[0]-origin[0],-(end[1]-origin[1]),0);
+	
+	updateWorkspace();
+}
+
+
+/*******************************************************************************
+ Use the current OpenGL viewing transforms to initialize a transform for the
+ haptic device workspace so that it's properly mapped to world coordinates.
+*******************************************************************************/
+void updateWorkspace(){
+    GLdouble modelview[16];
+    GLdouble projection[16];
+    GLint viewport[4];
+
+    glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+    glGetDoublev(GL_PROJECTION_MATRIX, projection);
+    glGetIntegerv(GL_VIEWPORT, viewport);
+	
+	hlMatrixMode(HL_TOUCHWORKSPACE);
+    hlLoadIdentity();
+    
+    // Fit haptic workspace to view volume.
+    hluFitWorkspace(projection);
+
+    // Compute cursor scale.
+	gManager->gCursorScale = hluScreenToModelScale(modelview, projection, viewport);
+	gManager->gCursorScale *= CURSOR_SIZE_PIXELS;
+
+	hduVector3Dd p0, p1;
+    bool bNoError;
+
+    bNoError = fromScreen(hduVector3Dd(0, 0, 0), p0);
+    assert(bNoError);
+    
+    bNoError = fromScreen(hduVector3Dd(1, 1, 0), p1);
+    assert(bNoError);
+
+    double m_windowTworldScale = (p1 - p0).magnitude() / sqrt(2.0);
+	gManager->gWorldScale = m_windowTworldScale;
+}
+/*******************************************************************************
+ GLUT callback for idle state.  Use this as an opportunity to request a redraw.
+ Checks for HLAPI errors that have occurred since the last idle check.
+*******************************************************************************/
+void idleCallback(){
+    HLerror error;
+
+    while (HL_ERROR(error = hlGetError())){
+		std::cerr <<  "HL Error: " << error.errorCode << std::endl <<error.errorInfo << std::endl;
+        
+        if (error.errorCode == HL_DEVICE_ERROR){
+            hduPrintError(stderr, &error.errorInfo, "Device error\n");
+			std::cout << "sending error message to haptic listener" << std::endl;
+			hapticCommandCB('e',0,0,0,0,0); 
+        }
+    }
+    glutPostRedisplay();
+}
+
+/*******************************************************************************
+ This handler is called when the application is exiting.  Deallocates any state 
+ and cleans up.
+*******************************************************************************/
+void exitProcedure(){
+    
+    // Free up the haptic rendering context.
+    hlMakeCurrent(NULL);
+	
+	if (HapticManager::ghHLRC != NULL){
+        hlDeleteContext(HapticManager::ghHLRC);
+    }
+
+    // Free up the haptic device.
+    if (HapticManager::ghHD != HD_INVALID_HANDLE){
+        hdDisableDevice(HapticManager::ghHD);
+    }
+	std::cout << "freeing haptic resources" << std::endl;
+}
+
+/* initialize all the variable needed for the jni access to the Haptics class from the openGL thread*/
+void initJniVariables(void){
+	/* --- CLASSES --- */
+	//this class
+	hapticClass = env->GetObjectClass(*lock);
+	if(hapticClass == NULL){
+		stopExecution("Could not find the Haptics class");
+	}
+	// the haptic listener, member of this class
+	hapticListenerClass = env->FindClass("Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
+	if(hapticListenerClass == NULL){
+		stopExecution("Could not find the haptic listener class");
+	}
+
+	/* --- FIELD IDS --- */
+	// boolean set by the java thread when an element is added and it needs a new id from the haptic library
+	newHapticIdfieldId = env->GetFieldID(hapticClass,"newHapticId", "Z");
+	if(newHapticIdfieldId == NULL){
+		stopExecution("failed to find the newHapticId field id");
+	}
+
+	// boolean set by the java thread when an element is added and it needs a new id from the haptic library
+	dumpHapticIdfieldId = env->GetFieldID(hapticClass,"dumpHapticId", "Z");
+	if(dumpHapticIdfieldId == NULL){
+		stopExecution("failed to find the dumpHapticId field id");
+	}
+
+	// boolean set to this thread to notify the java thread the unsuccessful initialization of haptic device
+	hapticInitFailedfieldId = env->GetFieldID(hapticClass,"hapticInitFailed", "Z");
+	if(hapticInitFailedfieldId == NULL){
+		stopExecution("failed to find the hapticInitFailedfieldId field id");
+	}
+
+	// boolean set by the java thread to notify this thread the program has been shut down 
+	shutdownfieldId = env->GetFieldID(hapticClass, "shutdown", "Z");
+	if(shutdownfieldId == NULL){
+		stopExecution("failed to find the shutdownfieldId field id");
+	}
+
+	// boolean set by the java thread when the user asks to sna
+	attractTofieldId = env->GetFieldID(hapticClass, "attractTo" , "Z");
+	if(attractTofieldId == NULL){
+		stopExecution("failed to find the attractTo field id");
+	}
+
+	attractToHapticIdFieldId = env->GetFieldID(hapticClass, "attractToHapticId", "I");
+	if(attractToHapticIdFieldId == NULL){
+		stopExecution("failed to find the attractToHapticId field id");
+	}
+
+	pickUpfieldId = env->GetFieldID(hapticClass,"pickUp","Z");
+	if(pickUpfieldId == NULL){
+		stopExecution("failed to find the pickUp field id");
+	}
+
+	pickUpHapticIdFieldId = env->GetFieldID(hapticClass,"pickUpHapticId","I");
+	if(pickUpHapticIdFieldId == NULL){
+		stopExecution("failed to find pickUpHapticId field id");
+	}
+
+	hapticListenerFieldId = env->GetFieldID(hapticClass, "hapticListener", "Luk/ac/qmul/eecs/ccmi/haptics/HapticListenerThread;");
+	if(hapticListenerFieldId == NULL){
+		stopExecution("failed to find the hapticListenerThread field id");
+	}
+
+	// variable to exchange values between threads
+	currentHapticIdfieldId = env->GetFieldID(hapticClass,"currentHapticId","I");
+	if(currentHapticIdfieldId == NULL){
+		stopExecution("failed to find the currentHapticId field");
+	}
+
+	cmdFieldId = env->GetFieldID(hapticListenerClass,"cmd", "C");
+	if(cmdFieldId == NULL){
+		stopExecution("failed to find the cmd field id of the hapticListener class");
+	}
+
+	diagramElementFieldId = env->GetFieldID(hapticListenerClass, "diagramElementID", "I");
+	if(diagramElementFieldId == NULL){
+		stopExecution("failed to find the diagramElement field id of the hapticListener class");
+	}
+
+	xFieldId = env->GetFieldID(hapticListenerClass, "x", "D");
+	if(xFieldId == NULL){
+		stopExecution("failed to find the x field id of the hapticListener class");
+	}
+
+	yFieldId = env->GetFieldID(hapticListenerClass, "y", "D");
+	if(yFieldId == NULL){
+		stopExecution("failed to find the y field id of the hapticListener class");
+	}
+
+	startXFieldId = env->GetFieldID(hapticListenerClass, "startX", "D");
+	if(startXFieldId == NULL){
+		stopExecution("failed to find the x field id of the hapticListener class");
+	}
+
+	startYFieldId = env->GetFieldID(hapticListenerClass, "startY", "D");
+	if(startYFieldId == NULL){
+		stopExecution("failed to find the y field id of the hapticListener class");
+	}
+
+	hapticListener = env->GetObjectField(*lock,hapticListenerFieldId);
+	/* --- METHOD IDs --- */
+	// notify method
+	notifyMethodId = env->GetMethodID(hapticClass,"notify","()V");
+	if(notifyMethodId == NULL){
+		stopExecution("failed to find the notify method id");
+	}
+
+	notifyListenerMethodId = env->GetMethodID(hapticListenerClass,"notify","()V");
+	if(notifyListenerMethodId == NULL){
+		stopExecution("failed to find the notify method id");
+	}
+
+	waitMethodId = env->GetMethodID(hapticListenerClass,"wait","()V");
+	if(waitMethodId == NULL){
+		stopExecution("failed to find the wait method id");
+	}
+}
+
+void hapticCommandCB(const jchar cmd, const jint ID, const jdouble x, const jdouble y, const jdouble startX, const jdouble startY){
+	/* the haptic listener java thread is waiting for commands
+	   first set the variable, the Haptic Listener java thread will read after being notified,
+	   then notify and get it awake. Thus wait for the java thread to notify that the command 
+	   has been accomplished. This is done as otherwise some commands might be neglected. as if the thread 
+	   scheduler decides to execute twice this routine without executing the java thread in the middle then 
+	   the former command gets overwritten by the latter. 
+	   Note the monitor is hapticListener and not haptics as for the draw function. 
+	   When in this routine, this thread does not hold the lock on haptics as if a command results in changing
+	   the elements collections (e.g. moveNode) all those methods are synchronized and require to acquire the lock on haptics.
+	   Since this thread would wait for the command to be executed by the java thread, which in turns would wait for this
+	   thread to release the lock on haptics, that would result in a deadlock. 
+	 */
+	/* now wake up the haptic listener */
+	if(env->MonitorEnter(hapticListener) != JNI_OK){
+		stopExecution("Could not allocate memory for haptic listener thread monitor");
+	}
+	checkExceptions(env,"Could not enter monitor on the haptic listener");
+	env->SetCharField(hapticListener,cmdFieldId,cmd);
+	env->SetIntField(hapticListener,diagramElementFieldId,ID);
+	env->SetDoubleField(hapticListener,xFieldId,x);
+	env->SetDoubleField(hapticListener,yFieldId,y);
+	env->SetDoubleField(hapticListener,startXFieldId,startX);
+	env->SetDoubleField(hapticListener,startYFieldId,startY);
+	// wake the java thread up to execute the command
+	env->CallVoidMethod(hapticListener,notifyListenerMethodId);
+	checkExceptions(env, "Could not call notify() on HapticListener");
+	/* wait for the commands to be executed. Here is actually where the monitor is
+	 * freed and the java thread starts to execute the command, having been notified */ 
+	env->CallVoidMethod(hapticListener,waitMethodId);
+	checkExceptions(env, "Could not call wait() on HapticListener");
+	if(env->MonitorExit(hapticListener) != JNI_OK){
+		stopExecution("Could not release memory for haptic listener thread monitor");
+	}
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/native/PhantomOmni/uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics.h	Tue Jul 10 22:39:37 2012 +0100
@@ -0,0 +1,27 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics */
+
+#ifndef _Included_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
+#define _Included_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MIN_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MIN_PRIORITY 1L
+#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_NORM_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_NORM_PRIORITY 5L
+#undef uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MAX_PRIORITY
+#define uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_MAX_PRIORITY 10L
+/*
+ * Class:     uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics
+ * Method:    init
+ * Signature: (II)I
+ */
+JNIEXPORT jint JNICALL Java_uk_ac_qmul_eecs_ccmi_haptics_OmniHaptics_initOmni
+  (JNIEnv *, jobject, jint, jint);
+
+#ifdef __cplusplus
+}
+#endif
+#endif