fiore@0: /* fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool fiore@0: fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: package uk.ac.qmul.eecs.ccmi.gui; fiore@0: fiore@0: import java.awt.geom.Point2D; fiore@0: import java.awt.geom.Rectangle2D; fiore@0: fiore@0: import javax.swing.SwingUtilities; fiore@0: fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel; fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement; fiore@0: import uk.ac.qmul.eecs.ccmi.haptics.HapticListener; fiore@0: import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand; fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; fiore@0: import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; fiore@0: import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; fiore@0: fiore@0: /** fiore@0: * fiore@0: * An instance of HapticListener for the diagram editor. By this class visual diagrams fiore@0: * can be manipulated by an haptic device. fiore@0: * fiore@0: */ fiore@0: public class HapticKindle extends HapticListener { fiore@0: fiore@0: public HapticKindle(){ fiore@0: super(); fiore@0: unselectRunnable = new Runnable(){ fiore@0: @Override fiore@0: public void run(){ fiore@0: frame.selectHapticHighligh(null); fiore@0: } fiore@0: }; fiore@0: frameBackupRunnable = new Runnable(){ fiore@0: @Override fiore@0: public void run(){ fiore@0: frame.backupOpenDiagrams(); fiore@0: } fiore@0: }; fiore@0: } fiore@0: fiore@0: public void setEditorFrame(EditorFrame frame){ fiore@0: this.frame = frame; fiore@0: } fiore@0: fiore@0: @Override fiore@0: public void executeCommand(HapticListenerCommand cmd, int ID, final double x, final double y, final double startX, final double startY) { fiore@0: if((frame == null)||(frame.getActiveTab() == null)) fiore@0: return; fiore@0: CollectionModel collectionModel = frame.getActiveTab().getDiagram().getCollectionModel(); fiore@0: Object monitor = collectionModel.getMonitor(); fiore@0: DiagramElement de = null; fiore@0: switch(cmd){ fiore@0: case PLAY_ELEMENT_SOUND : fiore@0: synchronized(monitor){ fiore@0: de = Finder.findElementByHashcode(ID, collectionModel.getElements()); fiore@0: } fiore@0: /* can be null if the tab has been switched or closed in the meantime */ fiore@0: if(de == null) fiore@0: return; fiore@0: SoundFactory.getInstance().play(de.getSound()); fiore@0: break; fiore@0: case PLAY_ELEMENT_SPEECH : fiore@0: synchronized(monitor){ fiore@0: de = Finder.findElementByHashcode(ID, collectionModel.getElements()); fiore@0: } fiore@0: if(de == null) fiore@0: return; fiore@0: SoundFactory.getInstance().play(de.getSound()); fiore@0: NarratorFactory.getInstance().speak(de.getName()); fiore@0: iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName()); fiore@0: break; fiore@0: case SELECT : fiore@0: synchronized(monitor){ fiore@0: de = Finder.findElementByHashcode(ID, collectionModel.getElements()); fiore@0: } fiore@0: if(de == null) fiore@0: return; fiore@0: final DiagramElement selectedElement = de; fiore@0: SwingUtilities.invokeLater(new Runnable(){ fiore@0: @Override fiore@0: public void run(){ fiore@0: frame.selectHapticHighligh(selectedElement); fiore@0: } fiore@0: }); fiore@0: break; fiore@0: case UNSELECT : fiore@0: SwingUtilities.invokeLater(unselectRunnable); fiore@0: break; fiore@0: case MOVE : fiore@0: DiagramPanel dPanel = frame.getActiveTab(); fiore@0: if(dPanel == null) fiore@0: return; fiore@0: synchronized(monitor){ fiore@0: de = Finder.findElementByHashcode(ID, collectionModel.getElements()); fiore@0: } fiore@0: if(de == null) fiore@0: return; fiore@0: final DiagramElement moveSelectedElement = de; fiore@0: final DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater(); fiore@0: if(!modelUpdater.getLock(moveSelectedElement, Lock.MOVE)){ fiore@0: iLog("Could not get lock on element for motion", DiagramElement.toLogString(moveSelectedElement)); fiore@0: NarratorFactory.getInstance().speak("Object is being moved by another user"); fiore@0: return; fiore@0: } fiore@0: fiore@0: try { fiore@0: SwingUtilities.invokeAndWait(new Runnable(){ fiore@0: @Override fiore@0: public void run(){ fiore@0: if(moveSelectedElement instanceof Node){ fiore@0: Node n = (Node)moveSelectedElement; fiore@0: Rectangle2D bounds = n.getBounds(); fiore@0: Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY()); fiore@0: double dx = x - p.getX(); fiore@0: double dy = y - p.getY(); fiore@0: modelUpdater.translate(n, p, dx, dy); fiore@0: modelUpdater.stopMove(n); fiore@0: fiore@0: StringBuilder builder = new StringBuilder(); fiore@0: builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX()) fiore@0: .append(' ').append(p.getY()); fiore@0: iLog("move node start",builder.toString()); fiore@0: builder = new StringBuilder(); fiore@0: builder.append(DiagramElement.toLogString(n)).append(' ') fiore@0: .append(x).append(' ').append(y); fiore@0: fiore@0: iLog("move node end",builder.toString()); fiore@0: fiore@0: }else{ fiore@0: Edge e = (Edge)moveSelectedElement; fiore@0: modelUpdater.startMove(e, new Point2D.Double(startX,startY)); fiore@0: Point2D p = new Point2D.Double(x,y); fiore@0: modelUpdater.bend(e, p); fiore@0: modelUpdater.stopMove(e); fiore@0: fiore@0: StringBuilder builder = new StringBuilder(); fiore@0: builder.append(DiagramElement.toLogString(e)).append(' ').append(startX) fiore@0: .append(' ').append(startY); fiore@0: iLog("bend edge start",builder.toString()); fiore@0: builder = new StringBuilder(); fiore@0: builder.append(DiagramElement.toLogString(e)).append(' ') fiore@0: .append(x).append(' ').append(y); fiore@0: iLog("bend edge end",builder.toString()); fiore@0: fiore@0: } fiore@0: } fiore@0: }); fiore@0: } catch (Exception e) { fiore@0: throw new RuntimeException(e); fiore@0: } fiore@0: SoundFactory.getInstance().play(SoundEvent.HOOK_OFF); fiore@0: modelUpdater.yieldLock(moveSelectedElement, Lock.MOVE); fiore@0: break; fiore@0: case INFO : fiore@0: synchronized(monitor){ fiore@0: de = Finder.findElementByHashcode(ID, collectionModel.getElements()); fiore@0: } fiore@0: if(de == null) fiore@0: return; fiore@0: SoundFactory.getInstance().stop(); fiore@0: NarratorFactory.getInstance().speak(de.detailedSpokenText()); fiore@0: iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName()); fiore@0: break; fiore@0: case PLAY_SOUND : fiore@0: switch(HapticListenerCommand.Sound.fromInt(ID) ){ fiore@0: case MAGNET_OFF : fiore@0: SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF); fiore@0: iLog("sticky mode off",""); fiore@0: break; fiore@0: case MAGNET_ON : fiore@0: SoundFactory.getInstance().play(SoundEvent.MAGNET_ON); fiore@0: iLog("sticky mode on",""); fiore@0: break; fiore@0: case HOOK_ON : fiore@0: SoundFactory.getInstance().play(SoundEvent.HOOK_ON); fiore@0: iLog("hook on",""); fiore@0: break; fiore@0: case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG); fiore@0: break; fiore@0: } fiore@0: break; fiore@0: case ERROR : fiore@0: /* no synchronization necessary as the XMLManager looks after it*/ fiore@0: SwingUtilities.invokeLater(frameBackupRunnable); fiore@0: NarratorFactory.getInstance().speak("Haptic device crashed. " + fiore@0: "Unsaved diagrams saved in backup directory"); fiore@0: break; fiore@0: } fiore@0: } fiore@0: fiore@0: private void iLog(String action, String args){ fiore@0: InteractionLog.log(INTERACTION_LOG_SOURCE,action,args); fiore@0: } fiore@0: fiore@0: private Runnable unselectRunnable; fiore@0: private Runnable frameBackupRunnable; fiore@0: private static String INTERACTION_LOG_SOURCE = "HAPTIC"; fiore@0: private EditorFrame frame; fiore@0: }