view java/src/uk/ac/qmul/eecs/ccmi/gui/HapticKindle.java @ 0:9418ab7b7f3f

Initial import
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Fri, 16 Dec 2011 17:35:51 +0000
parents
children 9e67171477bc
line wrap: on
line source
/*  
 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.gui;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import javax.swing.SwingUtilities;

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.HapticListenerCommand;
import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;

/**
 * 
 * An instance of HapticListener for the diagram editor. By this class visual diagrams 
 * can be manipulated by an haptic device.  
 *
 */
public class HapticKindle extends HapticListener {
	
	public HapticKindle(){
		super();
		unselectRunnable = new Runnable(){
			@Override
			public void run(){
				frame.selectHapticHighligh(null);
			}
		};
		frameBackupRunnable = new Runnable(){
			@Override
			public void run(){
				frame.backupOpenDiagrams();
			}
		};
	}

	public void setEditorFrame(EditorFrame frame){
		this.frame = frame;
	}
	
	@Override
	public void executeCommand(HapticListenerCommand cmd, int ID, final double x, final double y, final double startX, final double startY) {
		if((frame == null)||(frame.getActiveTab() == null))
			return;
		CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
		Object monitor = collectionModel.getMonitor();
		DiagramElement de = null;
		switch(cmd){
		case PLAY_ELEMENT_SOUND :
			synchronized(monitor){
				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 : 
			synchronized(monitor){
				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 :
			synchronized(monitor){
				de = Finder.findElementByHashcode(ID, collectionModel.getElements());
			}
			if(de == null)
				return;
			final DiagramElement selectedElement = de; 
			SwingUtilities.invokeLater(new Runnable(){
				@Override
				public void run(){
					frame.selectHapticHighligh(selectedElement);
				}
			});
			break;
		case UNSELECT :
			SwingUtilities.invokeLater(unselectRunnable);
			break;
		case MOVE :
			DiagramPanel dPanel = frame.getActiveTab();
			if(dPanel == null)
				return;
			synchronized(monitor){
				de = Finder.findElementByHashcode(ID, collectionModel.getElements());
			}
			if(de == null)
				return;
			final DiagramElement moveSelectedElement = de;
			final DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
			if(!modelUpdater.getLock(moveSelectedElement, Lock.MOVE)){
				iLog("Could not get lock on element for motion", DiagramElement.toLogString(moveSelectedElement));
				NarratorFactory.getInstance().speak("Object is being moved by another user");
				return;
			}
			
			try {
				SwingUtilities.invokeAndWait(new Runnable(){
					@Override
					public void run(){
						if(moveSelectedElement instanceof Node){
							Node n = (Node)moveSelectedElement;
							Rectangle2D bounds = n.getBounds();
							Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
							double dx = x - p.getX();
							double dy = y - p.getY();
							modelUpdater.translate(n, p, dx, dy);
							modelUpdater.stopMove(n);

							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)moveSelectedElement;
							modelUpdater.startMove(e, new Point2D.Double(startX,startY));
							Point2D p  = new Point2D.Double(x,y);
							modelUpdater.bend(e, p);
							modelUpdater.stopMove(e);

							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());
							
						}
					}
				});
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
			modelUpdater.yieldLock(moveSelectedElement, Lock.MOVE);
			break;
		case INFO :
			synchronized(monitor){
				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 HOOK_ON : 
				SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
				iLog("hook on","");
				break;
			case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
				break;
			}
			break;
		case ERROR : 
			/* no synchronization necessary as the XMLManager looks after it*/
			SwingUtilities.invokeLater(frameBackupRunnable);
			NarratorFactory.getInstance().speak("Haptic device crashed. " +
					"Unsaved diagrams saved in backup directory");
			break;
		}
	}
	
	private void iLog(String action, String args){
		InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
	}
	
	private Runnable unselectRunnable;
	private Runnable frameBackupRunnable;
	private static String INTERACTION_LOG_SOURCE = "HAPTIC"; 
	private EditorFrame frame;
}