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: }