comparison java/src/uk/ac/qmul/eecs/ccmi/gui/HapticKindle.java @ 3:9e67171477bc

PHANTOM Omni Heptic device release
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Wed, 25 Apr 2012 17:09:09 +0100
parents 9418ab7b7f3f
children d66dd5880081
comparison
equal deleted inserted replaced
2:4b2f975e35fa 3:9e67171477bc
18 */ 18 */
19 package uk.ac.qmul.eecs.ccmi.gui; 19 package uk.ac.qmul.eecs.ccmi.gui;
20 20
21 import java.awt.geom.Point2D; 21 import java.awt.geom.Point2D;
22 import java.awt.geom.Rectangle2D; 22 import java.awt.geom.Rectangle2D;
23 import java.util.ResourceBundle;
23 24
24 import javax.swing.SwingUtilities; 25 import javax.swing.SwingUtilities;
25 26
26 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel; 27 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel;
27 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; 28 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
28 import uk.ac.qmul.eecs.ccmi.haptics.HapticListener; 29 import uk.ac.qmul.eecs.ccmi.haptics.HapticListener;
29 import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand; 30 import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand;
31 import uk.ac.qmul.eecs.ccmi.main.DiagramEditorApp;
32 import uk.ac.qmul.eecs.ccmi.network.Command;
33 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource;
34 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
30 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; 35 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
31 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
32 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; 36 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
33 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; 37 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
34 38
35 /** 39 /**
36 * 40 *
43 public HapticKindle(){ 47 public HapticKindle(){
44 super(); 48 super();
45 unselectRunnable = new Runnable(){ 49 unselectRunnable = new Runnable(){
46 @Override 50 @Override
47 public void run(){ 51 public void run(){
52 EditorFrame frame = DiagramEditorApp.getFrame();
53 if((frame == null)||(frame.getActiveTab() == null))
54 return;
48 frame.selectHapticHighligh(null); 55 frame.selectHapticHighligh(null);
49 }
50 };
51 frameBackupRunnable = new Runnable(){
52 @Override
53 public void run(){
54 frame.backupOpenDiagrams();
55 } 56 }
56 }; 57 };
57 } 58 }
58 59
59 public void setEditorFrame(EditorFrame frame){ 60 /**
60 this.frame = frame; 61 * Implementation of the {@code executeCommand} method. All the commands that involve the
61 } 62 * diagram model, are executed in the Event Dispatching Thread through {@code SwingUtilities} invoke
62 63 * methods. This prevents race conditions on the model and on diagram elements.
64 *
65 * @see HapticListener#executeCommand(HapticListenerCommand, int, double, double, double, double)
66 */
63 @Override 67 @Override
64 public void executeCommand(HapticListenerCommand cmd, int ID, final double x, final double y, final double startX, final double startY) { 68 public void executeCommand(HapticListenerCommand cmd, final int ID, final double x, final double y, final double startX, final double startY) {
65 if((frame == null)||(frame.getActiveTab() == null)) 69 final EditorFrame frame = DiagramEditorApp.getFrame();
66 return;
67 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
68 Object monitor = collectionModel.getMonitor();
69 DiagramElement de = null;
70 switch(cmd){ 70 switch(cmd){
71 case PLAY_ELEMENT_SOUND : 71 case PLAY_ELEMENT_SOUND :
72 synchronized(monitor){ 72 SwingUtilities.invokeLater(new Runnable(){
73 de = Finder.findElementByHashcode(ID, collectionModel.getElements()); 73 public void run(){
74 } 74 if((frame == null)||(frame.getActiveTab() == null))
75 /* can be null if the tab has been switched or closed in the meantime */ 75 return;
76 if(de == null) 76 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
77 return; 77 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
78 SoundFactory.getInstance().play(de.getSound()); 78 /* can be null if the tab has been switched or closed in the meantime */
79 if(de == null)
80 return;
81 SoundFactory.getInstance().play(de.getSound());
82 }
83 });
79 break; 84 break;
80 case PLAY_ELEMENT_SPEECH : 85 case PLAY_ELEMENT_SPEECH :
81 synchronized(monitor){ 86 SwingUtilities.invokeLater(new Runnable(){
82 de = Finder.findElementByHashcode(ID, collectionModel.getElements()); 87 public void run(){
83 } 88 if((frame == null)||(frame.getActiveTab() == null))
84 if(de == null) 89 return;
85 return; 90 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
86 SoundFactory.getInstance().play(de.getSound()); 91 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
87 NarratorFactory.getInstance().speak(de.getName()); 92 if(de == null)
88 iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName()); 93 return;
94 SoundFactory.getInstance().play(de.getSound());
95 NarratorFactory.getInstance().speak(de.getName());
96 iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName());
97 }
98 });
89 break; 99 break;
90 case SELECT : 100 case SELECT :
91 synchronized(monitor){ 101 SwingUtilities.invokeLater(new Runnable(){
92 de = Finder.findElementByHashcode(ID, collectionModel.getElements()); 102 public void run(){
93 } 103 if((frame == null)||(frame.getActiveTab() == null))
94 if(de == null) 104 return;
95 return; 105 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
96 final DiagramElement selectedElement = de; 106 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
97 SwingUtilities.invokeLater(new Runnable(){ 107 if(de == null)
98 @Override 108 return;
99 public void run(){ 109 frame.selectHapticHighligh(de);
100 frame.selectHapticHighligh(selectedElement);
101 } 110 }
102 }); 111 });
103 break; 112 break;
104 case UNSELECT : 113 case UNSELECT :
105 SwingUtilities.invokeLater(unselectRunnable); 114 SwingUtilities.invokeLater(unselectRunnable);
106 break; 115 break;
107 case MOVE : 116 case MOVE : {
108 DiagramPanel dPanel = frame.getActiveTab(); 117 /* when this block is executed we already have the lock *
109 if(dPanel == null) 118 * on the element from the PICK_UP command execution */
110 return;
111 synchronized(monitor){
112 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
113 }
114 if(de == null)
115 return;
116 final DiagramElement moveSelectedElement = de;
117 final DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
118 if(!modelUpdater.getLock(moveSelectedElement, Lock.MOVE)){
119 iLog("Could not get lock on element for motion", DiagramElement.toLogString(moveSelectedElement));
120 NarratorFactory.getInstance().speak("Object is being moved by another user");
121 return;
122 }
123
124 try { 119 try {
125 SwingUtilities.invokeAndWait(new Runnable(){ 120 SwingUtilities.invokeAndWait(new Runnable(){
126 @Override 121 @Override
127 public void run(){ 122 public void run(){
128 if(moveSelectedElement instanceof Node){ 123 if((frame == null)||(frame.getActiveTab() == null))
129 Node n = (Node)moveSelectedElement; 124 return;
125 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
126 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
127 if(de == null)
128 return;
129 DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
130 if(de instanceof Node){
131 Node n = (Node)de;
130 Rectangle2D bounds = n.getBounds(); 132 Rectangle2D bounds = n.getBounds();
131 Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY()); 133 Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
132 double dx = x - p.getX(); 134 double dx = x - p.getX();
133 double dy = y - p.getY(); 135 double dy = y - p.getY();
134 modelUpdater.translate(n, p, dx, dy); 136 n.getMonitor().lock();
135 modelUpdater.stopMove(n); 137 modelUpdater.translate(n, p, dx, dy,DiagramEventSource.HAPT);
138 modelUpdater.stopMove(n,DiagramEventSource.HAPT);
139 n.getMonitor().unlock();
136 140
137 StringBuilder builder = new StringBuilder(); 141 StringBuilder builder = new StringBuilder();
138 builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX()) 142 builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX())
139 .append(' ').append(p.getY()); 143 .append(' ').append(p.getY());
140 iLog("move node start",builder.toString()); 144 iLog("move node start",builder.toString());
141 builder = new StringBuilder(); 145 builder = new StringBuilder();
142 builder.append(DiagramElement.toLogString(n)).append(' ') 146 builder.append(DiagramElement.toLogString(n)).append(' ')
143 .append(x).append(' ').append(y); 147 .append(x).append(' ').append(y);
144
145 iLog("move node end",builder.toString()); 148 iLog("move node end",builder.toString());
149 }else{
150 Edge e = (Edge)de;
151 modelUpdater.startMove(e, new Point2D.Double(startX,startY),DiagramEventSource.HAPT);
152 Point2D p = new Point2D.Double(x,y);
153 e.getMonitor().lock();
154 modelUpdater.bend(e, p,DiagramEventSource.HAPT);
155 modelUpdater.stopMove(e,DiagramEventSource.HAPT);
156 e.getMonitor().unlock();
146 157
147 }else{
148 Edge e = (Edge)moveSelectedElement;
149 modelUpdater.startMove(e, new Point2D.Double(startX,startY));
150 Point2D p = new Point2D.Double(x,y);
151 modelUpdater.bend(e, p);
152 modelUpdater.stopMove(e);
153
154 StringBuilder builder = new StringBuilder(); 158 StringBuilder builder = new StringBuilder();
155 builder.append(DiagramElement.toLogString(e)).append(' ').append(startX) 159 builder.append(DiagramElement.toLogString(e)).append(' ').append(startX)
156 .append(' ').append(startY); 160 .append(' ').append(startY);
157 iLog("bend edge start",builder.toString()); 161 iLog("bend edge start",builder.toString());
158 builder = new StringBuilder(); 162 builder = new StringBuilder();
159 builder.append(DiagramElement.toLogString(e)).append(' ') 163 builder.append(DiagramElement.toLogString(e)).append(' ')
160 .append(x).append(' ').append(y); 164 .append(x).append(' ').append(y);
161 iLog("bend edge end",builder.toString()); 165 iLog("bend edge end",builder.toString());
162
163 } 166 }
164 } 167 modelUpdater.yieldLock(de,
168 Lock.MOVE,
169 new DiagramEventActionSource(
170 DiagramEventSource.HAPT,
171 de instanceof Node ? Command.Name.STOP_NODE_MOVE : Command.Name.STOP_EDGE_MOVE,
172 de.getId(),
173 de.getName()
174 ));
175 } // run()
165 }); 176 });
166 } catch (Exception e) { 177 } catch (Exception e) {
167 throw new RuntimeException(e); 178 throw new RuntimeException(e);
168 } 179 }
169 SoundFactory.getInstance().play(SoundEvent.HOOK_OFF); 180 SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
170 modelUpdater.yieldLock(moveSelectedElement, Lock.MOVE); 181 }
171 break; 182 break;
172 case INFO : 183 case INFO :
173 synchronized(monitor){ 184 SwingUtilities.invokeLater(new Runnable(){
174 de = Finder.findElementByHashcode(ID, collectionModel.getElements()); 185 public void run(){
175 } 186 if((frame == null)||(frame.getActiveTab() == null))
176 if(de == null) 187 return;
177 return; 188 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
178 SoundFactory.getInstance().stop(); 189 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
179 NarratorFactory.getInstance().speak(de.detailedSpokenText()); 190 if(de == null)
180 iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName()); 191 return;
192 SoundFactory.getInstance().stop();
193 NarratorFactory.getInstance().speak(de.detailedSpokenText());
194 iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName());
195 }
196 });
181 break; 197 break;
182 case PLAY_SOUND : 198 case PLAY_SOUND :
183 switch(HapticListenerCommand.Sound.fromInt(ID) ){ 199 switch(HapticListenerCommand.Sound.fromInt(ID) ){
184 case MAGNET_OFF : 200 case MAGNET_OFF :
185 SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF); 201 SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF);
187 break; 203 break;
188 case MAGNET_ON : 204 case MAGNET_ON :
189 SoundFactory.getInstance().play(SoundEvent.MAGNET_ON); 205 SoundFactory.getInstance().play(SoundEvent.MAGNET_ON);
190 iLog("sticky mode on",""); 206 iLog("sticky mode on","");
191 break; 207 break;
192 case HOOK_ON :
193 SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
194 iLog("hook on","");
195 break;
196 case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG); 208 case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
197 break; 209 break;
198 } 210 }
199 break; 211 break;
212 case PICK_UP :
213 try {
214 SwingUtilities.invokeAndWait(new Runnable (){
215 @Override
216 public void run(){
217 if((frame == null)||(frame.getActiveTab() == null))
218 return;
219 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
220 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
221 if(de == null)
222 return;
223 DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
224 if(!modelUpdater.getLock(de,
225 Lock.MOVE,
226 new DiagramEventActionSource(DiagramEventSource.HAPT, de instanceof Edge ? Command.Name.TRANSLATE_EDGE : Command.Name.TRANSLATE_NODE ,de.getId(),de.getName()))){
227 iLog("Could not get lock on element for motion", DiagramElement.toLogString(de));
228 NarratorFactory.getInstance().speak("Object is being moved by another user");
229 return;
230 }
231 frame.hPickUp(de);
232 SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
233 iLog("hook on","");
234 }
235 });
236 }catch(Exception e){
237 e.printStackTrace();
238 throw new RuntimeException();
239 }
240 break;
200 case ERROR : 241 case ERROR :
201 /* no synchronization necessary as the XMLManager looks after it*/ 242 /* no synchronization necessary as the XMLManager looks after it*/
202 SwingUtilities.invokeLater(frameBackupRunnable); 243 SwingUtilities.invokeLater(new Runnable(){
203 NarratorFactory.getInstance().speak("Haptic device crashed. " + 244 @Override
204 "Unsaved diagrams saved in backup directory"); 245 public void run(){
246 if((frame == null)||(frame.getActiveTab() == null))
247 return;
248 frame.backupOpenDiagrams();
249 }
250 });
251 NarratorFactory.getInstance().speak(ResourceBundle.getBundle(EditorFrame.class.getName()).getString("speech.haptic_device_crashed"));
205 break; 252 break;
206 } 253 }
207 } 254 }
208 255
209 private void iLog(String action, String args){ 256 private void iLog(String action, String args){
210 InteractionLog.log(INTERACTION_LOG_SOURCE,action,args); 257 InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
211 } 258 }
212 259
213 private Runnable unselectRunnable; 260 private Runnable unselectRunnable;
214 private Runnable frameBackupRunnable;
215 private static String INTERACTION_LOG_SOURCE = "HAPTIC"; 261 private static String INTERACTION_LOG_SOURCE = "HAPTIC";
216 private EditorFrame frame; 262 //private EditorFrame frame;
217 } 263 }