annotate 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
rev   line source
fiore@0 1 /*
fiore@0 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
fiore@0 3
fiore@0 4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
fiore@0 5
fiore@0 6 This program is free software: you can redistribute it and/or modify
fiore@0 7 it under the terms of the GNU General Public License as published by
fiore@0 8 the Free Software Foundation, either version 3 of the License, or
fiore@0 9 (at your option) any later version.
fiore@0 10
fiore@0 11 This program is distributed in the hope that it will be useful,
fiore@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
fiore@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
fiore@0 14 GNU General Public License for more details.
fiore@0 15
fiore@0 16 You should have received a copy of the GNU General Public License
fiore@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
fiore@0 18 */
fiore@0 19 package uk.ac.qmul.eecs.ccmi.gui;
fiore@0 20
fiore@0 21 import java.awt.geom.Point2D;
fiore@0 22 import java.awt.geom.Rectangle2D;
fiore@3 23 import java.util.ResourceBundle;
fiore@0 24
fiore@0 25 import javax.swing.SwingUtilities;
fiore@0 26
fiore@0 27 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel;
fiore@0 28 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
fiore@0 29 import uk.ac.qmul.eecs.ccmi.haptics.HapticListener;
fiore@0 30 import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand;
fiore@3 31 import uk.ac.qmul.eecs.ccmi.main.DiagramEditorApp;
fiore@3 32 import uk.ac.qmul.eecs.ccmi.network.Command;
fiore@3 33 import uk.ac.qmul.eecs.ccmi.network.DiagramEventActionSource;
fiore@3 34 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
fiore@0 35 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
fiore@0 36 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
fiore@0 37 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
fiore@0 38
fiore@0 39 /**
fiore@0 40 *
fiore@0 41 * An instance of HapticListener for the diagram editor. By this class visual diagrams
fiore@0 42 * can be manipulated by an haptic device.
fiore@0 43 *
fiore@0 44 */
fiore@0 45 public class HapticKindle extends HapticListener {
fiore@0 46
fiore@0 47 public HapticKindle(){
fiore@0 48 super();
fiore@0 49 unselectRunnable = new Runnable(){
fiore@0 50 @Override
fiore@0 51 public void run(){
fiore@3 52 EditorFrame frame = DiagramEditorApp.getFrame();
fiore@3 53 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 54 return;
fiore@0 55 frame.selectHapticHighligh(null);
fiore@0 56 }
fiore@0 57 };
fiore@0 58 }
fiore@0 59
fiore@3 60 /**
fiore@3 61 * Implementation of the {@code executeCommand} method. All the commands that involve the
fiore@3 62 * diagram model, are executed in the Event Dispatching Thread through {@code SwingUtilities} invoke
fiore@3 63 * methods. This prevents race conditions on the model and on diagram elements.
fiore@3 64 *
fiore@3 65 * @see HapticListener#executeCommand(HapticListenerCommand, int, double, double, double, double)
fiore@3 66 */
fiore@0 67 @Override
fiore@3 68 public void executeCommand(HapticListenerCommand cmd, final int ID, final double x, final double y, final double startX, final double startY) {
fiore@3 69 final EditorFrame frame = DiagramEditorApp.getFrame();
fiore@0 70 switch(cmd){
fiore@3 71 case PLAY_ELEMENT_SOUND :
fiore@3 72 SwingUtilities.invokeLater(new Runnable(){
fiore@3 73 public void run(){
fiore@3 74 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 75 return;
fiore@3 76 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 77 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 78 /* can be null if the tab has been switched or closed in the meantime */
fiore@3 79 if(de == null)
fiore@3 80 return;
fiore@3 81 SoundFactory.getInstance().play(de.getSound());
fiore@3 82 }
fiore@3 83 });
fiore@0 84 break;
fiore@0 85 case PLAY_ELEMENT_SPEECH :
fiore@3 86 SwingUtilities.invokeLater(new Runnable(){
fiore@3 87 public void run(){
fiore@3 88 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 89 return;
fiore@3 90 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 91 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 92 if(de == null)
fiore@3 93 return;
fiore@3 94 SoundFactory.getInstance().play(de.getSound());
fiore@3 95 NarratorFactory.getInstance().speak(de.getName());
fiore@3 96 iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName());
fiore@3 97 }
fiore@3 98 });
fiore@0 99 break;
fiore@0 100 case SELECT :
fiore@0 101 SwingUtilities.invokeLater(new Runnable(){
fiore@0 102 public void run(){
fiore@3 103 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 104 return;
fiore@3 105 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 106 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 107 if(de == null)
fiore@3 108 return;
fiore@3 109 frame.selectHapticHighligh(de);
fiore@0 110 }
fiore@0 111 });
fiore@0 112 break;
fiore@0 113 case UNSELECT :
fiore@0 114 SwingUtilities.invokeLater(unselectRunnable);
fiore@0 115 break;
fiore@3 116 case MOVE : {
fiore@3 117 /* when this block is executed we already have the lock *
fiore@3 118 * on the element from the PICK_UP command execution */
fiore@0 119 try {
fiore@0 120 SwingUtilities.invokeAndWait(new Runnable(){
fiore@0 121 @Override
fiore@0 122 public void run(){
fiore@3 123 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 124 return;
fiore@3 125 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 126 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 127 if(de == null)
fiore@3 128 return;
fiore@3 129 DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
fiore@3 130 if(de instanceof Node){
fiore@3 131 Node n = (Node)de;
fiore@0 132 Rectangle2D bounds = n.getBounds();
fiore@0 133 Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
fiore@0 134 double dx = x - p.getX();
fiore@0 135 double dy = y - p.getY();
fiore@3 136 n.getMonitor().lock();
fiore@3 137 modelUpdater.translate(n, p, dx, dy,DiagramEventSource.HAPT);
fiore@3 138 modelUpdater.stopMove(n,DiagramEventSource.HAPT);
fiore@3 139 n.getMonitor().unlock();
fiore@0 140
fiore@0 141 StringBuilder builder = new StringBuilder();
fiore@0 142 builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX())
fiore@0 143 .append(' ').append(p.getY());
fiore@0 144 iLog("move node start",builder.toString());
fiore@0 145 builder = new StringBuilder();
fiore@0 146 builder.append(DiagramElement.toLogString(n)).append(' ')
fiore@0 147 .append(x).append(' ').append(y);
fiore@0 148 iLog("move node end",builder.toString());
fiore@3 149 }else{
fiore@3 150 Edge e = (Edge)de;
fiore@3 151 modelUpdater.startMove(e, new Point2D.Double(startX,startY),DiagramEventSource.HAPT);
fiore@3 152 Point2D p = new Point2D.Double(x,y);
fiore@3 153 e.getMonitor().lock();
fiore@3 154 modelUpdater.bend(e, p,DiagramEventSource.HAPT);
fiore@3 155 modelUpdater.stopMove(e,DiagramEventSource.HAPT);
fiore@3 156 e.getMonitor().unlock();
fiore@0 157
fiore@0 158 StringBuilder builder = new StringBuilder();
fiore@0 159 builder.append(DiagramElement.toLogString(e)).append(' ').append(startX)
fiore@0 160 .append(' ').append(startY);
fiore@0 161 iLog("bend edge start",builder.toString());
fiore@0 162 builder = new StringBuilder();
fiore@0 163 builder.append(DiagramElement.toLogString(e)).append(' ')
fiore@0 164 .append(x).append(' ').append(y);
fiore@0 165 iLog("bend edge end",builder.toString());
fiore@0 166 }
fiore@3 167 modelUpdater.yieldLock(de,
fiore@3 168 Lock.MOVE,
fiore@3 169 new DiagramEventActionSource(
fiore@3 170 DiagramEventSource.HAPT,
fiore@3 171 de instanceof Node ? Command.Name.STOP_NODE_MOVE : Command.Name.STOP_EDGE_MOVE,
fiore@3 172 de.getId(),
fiore@3 173 de.getName()
fiore@3 174 ));
fiore@3 175 } // run()
fiore@0 176 });
fiore@0 177 } catch (Exception e) {
fiore@0 178 throw new RuntimeException(e);
fiore@0 179 }
fiore@0 180 SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
fiore@3 181 }
fiore@0 182 break;
fiore@3 183 case INFO :
fiore@3 184 SwingUtilities.invokeLater(new Runnable(){
fiore@3 185 public void run(){
fiore@3 186 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 187 return;
fiore@3 188 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 189 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 190 if(de == null)
fiore@3 191 return;
fiore@3 192 SoundFactory.getInstance().stop();
fiore@3 193 NarratorFactory.getInstance().speak(de.detailedSpokenText());
fiore@3 194 iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName());
fiore@3 195 }
fiore@3 196 });
fiore@0 197 break;
fiore@0 198 case PLAY_SOUND :
fiore@0 199 switch(HapticListenerCommand.Sound.fromInt(ID) ){
fiore@0 200 case MAGNET_OFF :
fiore@0 201 SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF);
fiore@0 202 iLog("sticky mode off","");
fiore@0 203 break;
fiore@0 204 case MAGNET_ON :
fiore@0 205 SoundFactory.getInstance().play(SoundEvent.MAGNET_ON);
fiore@0 206 iLog("sticky mode on","");
fiore@0 207 break;
fiore@0 208 case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
fiore@0 209 break;
fiore@0 210 }
fiore@0 211 break;
fiore@3 212 case PICK_UP :
fiore@3 213 try {
fiore@3 214 SwingUtilities.invokeAndWait(new Runnable (){
fiore@3 215 @Override
fiore@3 216 public void run(){
fiore@3 217 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 218 return;
fiore@3 219 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
fiore@3 220 DiagramElement de = Finder.findElementByHashcode(ID, collectionModel.getElements());
fiore@3 221 if(de == null)
fiore@3 222 return;
fiore@3 223 DiagramModelUpdater modelUpdater = frame.getActiveTab().getDiagram().getModelUpdater();
fiore@3 224 if(!modelUpdater.getLock(de,
fiore@3 225 Lock.MOVE,
fiore@3 226 new DiagramEventActionSource(DiagramEventSource.HAPT, de instanceof Edge ? Command.Name.TRANSLATE_EDGE : Command.Name.TRANSLATE_NODE ,de.getId(),de.getName()))){
fiore@3 227 iLog("Could not get lock on element for motion", DiagramElement.toLogString(de));
fiore@3 228 NarratorFactory.getInstance().speak("Object is being moved by another user");
fiore@3 229 return;
fiore@3 230 }
fiore@3 231 frame.hPickUp(de);
fiore@3 232 SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
fiore@3 233 iLog("hook on","");
fiore@3 234 }
fiore@3 235 });
fiore@3 236 }catch(Exception e){
fiore@3 237 e.printStackTrace();
fiore@3 238 throw new RuntimeException();
fiore@3 239 }
fiore@3 240 break;
fiore@0 241 case ERROR :
fiore@0 242 /* no synchronization necessary as the XMLManager looks after it*/
fiore@3 243 SwingUtilities.invokeLater(new Runnable(){
fiore@3 244 @Override
fiore@3 245 public void run(){
fiore@3 246 if((frame == null)||(frame.getActiveTab() == null))
fiore@3 247 return;
fiore@3 248 frame.backupOpenDiagrams();
fiore@3 249 }
fiore@3 250 });
fiore@3 251 NarratorFactory.getInstance().speak(ResourceBundle.getBundle(EditorFrame.class.getName()).getString("speech.haptic_device_crashed"));
fiore@0 252 break;
fiore@0 253 }
fiore@0 254 }
fiore@0 255
fiore@0 256 private void iLog(String action, String args){
fiore@0 257 InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
fiore@0 258 }
fiore@0 259
fiore@0 260 private Runnable unselectRunnable;
fiore@0 261 private static String INTERACTION_LOG_SOURCE = "HAPTIC";
fiore@3 262 //private EditorFrame frame;
fiore@0 263 }