Mercurial > hg > accesspd
view java/src/uk/ac/qmul/eecs/ccmi/haptics/MouseHaptics.java @ 1:e3935c01cde2 tip
moved license of PdPersistenceManager to the beginning of the file
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Tue, 08 Jul 2014 19:52:03 +0100 |
parents | 78b7fc5391a2 |
children |
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.haptics; import java.awt.AWTException; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Robot; import java.awt.Stroke; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowFocusListener; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.io.IOException; import java.util.BitSet; import javax.swing.JFrame; import javax.swing.JPanel; import uk.ac.qmul.eecs.ccmi.gui.DiagramPanel; import uk.ac.qmul.eecs.ccmi.main.DiagramEditorApp; import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; @SuppressWarnings("serial") class MouseHaptics extends JPanel implements Haptics, KeyListener, MouseListener, MouseMotionListener { static Haptics createInstance(HapticListener listener){ MouseHaptics instance = new MouseHaptics(listener); try{ instance.initMouseHaptics(); return instance; }catch(IOException ioe){ return null; } } private MouseHaptics(HapticListener listener){ this.listener = listener; solidStroke = new BasicStroke(EDGE_THICKNESS); dottedStroke = new BasicStroke(EDGE_THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[]{1.0f,30.0f}, 0.0f); dashedStroke = new BasicStroke(EDGE_THICKNESS, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[]{80.0f,50.0f}, 0.0f); } @Override public void paintComponent(Graphics g){ super.paintComponent(g); Graphics2D g2 = (Graphics2D)g; Stroke oldStroke = g2.getStroke(); // draw edges g2.setColor(EDGE_COLOR); for(Edge e : listSupport.getCurrentEdges()){ switch(e.stipplePattern){ case Edge.SOLID_LINE : g2.setStroke(solidStroke);break; case Edge.DOTTED_LINE : g2.setStroke(dottedStroke);break; case Edge.DASHED_LINE : g2.setStroke(dashedStroke);break; } for(int i=0; i< e.adjMatrix.length; i++){ BitSet adj = e.adjMatrix[i]; for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) { g2.drawLine((int)e.xs[i], (int)e.ys[i], (int)e.xs[j], (int)e.ys[j]); } } } g2.setStroke(oldStroke); // draw nodes g2.setColor(NODE_COLOR); for(Node n : listSupport.getCurrentNodes()){ g2.fillOval((int)(n.x - NODE_DIAMETER/2), (int)(n.y - NODE_DIAMETER/2), NODE_DIAMETER, NODE_DIAMETER); } } @Override public void run() {} void initMouseHaptics() throws IOException { hapticFrame = new JFrame("Haptic Frame"); try { robot = new Robot(); } catch (AWTException e) { throw new IOException(e); } /* this is necessary to make the window screen size. it doens't work with the default layout */ setLayout(new BorderLayout()); setBackground(Color.black); listSupport = new HapticListSupport(); hapticFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); hapticFrame.addWindowFocusListener(new WindowFocusListener(){ @Override public void windowGainedFocus(WindowEvent arg0) { NarratorFactory.getInstance().speak("Haptic window Focused"); } @Override public void windowLostFocus(WindowEvent arg0) {} }); hapticFrame.setContentPane(this); hapticFrame.setExtendedState(JFrame.MAXIMIZED_BOTH); hapticFrame.setUndecorated(true); hapticFrame.addKeyListener(this); addMouseMotionListener(this); addMouseListener(this); hapticFrame.pack(); } @Override public void addNewDiagram(String diagramName) { listSupport.addNewDiagram(diagramName); repaint(); } @Override public void switchDiagram(String diagramName) { listSupport.switchDiagram(diagramName); repaint(); } @Override public void removeDiagram(String diagramNameToRemove, String diagramNameOfNext) { listSupport.removeDiagram(diagramNameToRemove, diagramNameOfNext); repaint(); } @Override public void addNode(double x, double y, int nodeHashCode, String diagramName) { Node n = new Node(x,y,nodeHashCode, 0); listSupport.addNode(n,diagramName); repaint(); } @Override public void removeNode(int nodeHashCode, String diagramName) { listSupport.removeNode(nodeHashCode, diagramName); repaint(); } @Override public void moveNode(double x, double y, int nodeHashCode, String diagramName) { Node n = listSupport.getNode(nodeHashCode, diagramName); n.x = x; n.y = y; repaint(); } @Override public void addEdge(int edgeHashCode, double[] xs, double[] ys, BitSet[] adjMatrix, int nodeStart, int stipplePattern, Line2D attractLine, String diagramName) { // find the mid point of the line of attraction double pX = Math.min(attractLine.getX1(), attractLine.getX2()); double pY = Math.min(attractLine.getY1(), attractLine.getY2()); pX += Math.abs(attractLine.getX1() - attractLine.getX2())/2; pY += Math.abs(attractLine.getY1() - attractLine.getY2())/2; Edge e = new Edge(edgeHashCode,0, xs, ys, adjMatrix, nodeStart, stipplePattern, pX, pY); listSupport.addEdge(e, diagramName); repaint(); } @Override public void updateEdge(int edgeHashCode, double[] xs, double[] ys, BitSet[] adjMatrix, int nodeStart, Line2D attractLine, String diagramName) { Edge e = listSupport.getEdge(edgeHashCode, diagramName); e.xs = xs; e.ys = ys; e.adjMatrix = adjMatrix; e.nodeStart = nodeStart; repaint(); } @Override public void removeEdge(int edgeHashCode, String diagramName) { listSupport.removeEdge(edgeHashCode, diagramName); repaint(); } @Override public void attractTo(int elementHashCode) { } @Override public void pickUp(int elementHashCode) { // needed to change the status of the haptic device. not needed here } @Override public boolean isAlive() { return false; } @Override public void setVisible(boolean visible){ hapticFrame.setVisible(visible); } @Override public void dispose() { // no resources to free up } @Override public void keyPressed(KeyEvent evt) { DiagramPanel panel = DiagramEditorApp.getFrame().getActiveTab(); if(panel == null) DiagramEditorApp.getFrame().editorTabbedPane.dispatchEvent(evt); else panel.getTree().dispatchEvent(evt); } @Override public void keyReleased(KeyEvent evt) { keyPressed(evt); } @Override public void keyTyped(KeyEvent evt) { keyPressed(evt); } @Override public void mouseDragged(MouseEvent evt) { /* priority to nodes: check if the mouse pointer is inside a circle centred on * * the node and with radius equal to NODE_RADIUS. If the mouse pointer is within * * the radius of two or more nodes then the closest is picked up. The command is * * executed once when the node is touched. In order have it executed again the * * used must get away from it and hover above it again */ Point2D p = evt.getPoint(); if(elementPickedUp){ draggedDistance += evt.getPoint().distance(lastDragPoint); if( draggedDistance > CHAIN_SOUND_INTERVAL){ listener.executeCommand(HapticListenerCommand.PLAY_SOUND, 3, 0,0,0,0); draggedDistance = 0; } lastDragPoint = evt.getPoint(); } Node hoveredNode = null; for(Node n : listSupport.getCurrentNodes()){ double distance = p.distance(n.x, n.y); if( distance < NODE_HOVER_DIST){ if(hoveredNode == null || distance < p.distance(hoveredNode.x,hoveredNode.y) ){ hoveredNode = n; } } } if(hoveredNode != null && hoveredNode != lastTouchedNode){ listener.executeCommand(HapticListenerCommand.PLAY_ELEMENT_SPEECH, hoveredNode.diagramId, 0, 0, 0, 0); lastTouchedNode = hoveredNode; lastTouchedEdge = null; return; } lastTouchedNode = hoveredNode; /* if hovering inside a node neither send the command nor take edges into account */ if(hoveredNode != null) return; /* if no node is being touched, check the edges out. */ Edge hoveredEdge = null; Line2D line = new Line2D.Double(); /* look at all edges */ for(Edge e : listSupport.getCurrentEdges()){ /* look at all edge's lines */ for(int i=0; i< e.adjMatrix.length; i++){ BitSet adj = e.adjMatrix[i]; for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) { line.setLine(e.xs[i], e.ys[i], e.xs[j], e.ys[j]); if(lastTouchedEdge != e && line.ptSegDist(p)<EDGE_HOVER_DIST){ hoveredEdge = e; listener.executeCommand(HapticListenerCommand.PLAY_ELEMENT_SPEECH, hoveredEdge.diagramId, 0, 0, 0, 0); lastTouchedEdge = hoveredEdge; return; } } } } lastTouchedEdge = hoveredEdge; } @Override public void mouseMoved(MouseEvent evt) { /* right click on a graphic tablet used as mouse has the effect of nullifying the * * dragging. That is there is no right click but rather it's like if you untouch * * the tablet. In order to address this a robot is used in order to re-leftclick each time * * the right click is pressed. In this way we assure that the left click is always held * * and therefore the mouse is always dragging rather than moving */ if(mustReclick){ mustReclick = false; reclickedAfterMove = true; // this is to make this.mousePressed() have no effect, when it's the robot clicking robot.mousePress(InputEvent.BUTTON1_MASK); } } @Override public void mousePressed(MouseEvent evt) { /* by clicking on the object, its name is stated by the TTS. * * Much as what happens when hovering on it with the button pressed */ if(evt.getButton() == MouseEvent.BUTTON1){ if(reclickedAfterMove){ reclickedAfterMove = false; return; } lastTouchedEdge = null; // these two fields are used with dragging to avoid repeating lastTouchedNode = null; mouseDragged(evt);//left clicking on an object is like to drag on it return; } /* button 3 (right click) is for moving the objects (picking up and dropping) */ if(evt.getButton() != MouseEvent.BUTTON3) return; if(!secondClick){ // clicked for the first time: pick up the node or edge Point2D p = evt.getPoint(); Node hoveredNode = null; for(Node n : listSupport.getCurrentNodes()){ double distance = p.distance(n.x, n.y); if( distance < NODE_HOVER_DIST){ if(hoveredNode == null || distance < p.distance(hoveredNode.x,hoveredNode.y) ){ hoveredNode = n; } } } if(hoveredNode != null){ // clicked on a node listener.executeCommand(HapticListenerCommand.PICK_UP, hoveredNode.diagramId, 0, 0, 0, 0); secondClick = true; startX = evt.getX(); startY = evt.getY(); pickedUpElementId = hoveredNode.diagramId; mustReclick = true; /* sets the variables for the chain sound when dragging the element around */ elementPickedUp = true; lastDragPoint = evt.getPoint(); return; } /* if no node is being touched, check the edges out. */ Line2D line = new Line2D.Double(); /* look at all edges */ for(Edge e : listSupport.getCurrentEdges()){ /* look at all edge's lines */ for(int i=0; i< e.adjMatrix.length; i++){ BitSet adj = e.adjMatrix[i]; for (int j = adj.nextSetBit(0); j >= 0; j = adj.nextSetBit(j+1)) { line.setLine(e.xs[i], e.ys[i], e.xs[j], e.ys[j]); if(/*lastTouchedEdge != e && */line.ptSegDist(p)<EDGE_HOVER_DIST){ listener.executeCommand(HapticListenerCommand.PICK_UP, e.diagramId, 0, 0, 0, 0); secondClick = true; startX = evt.getX(); startY = evt.getY(); pickedUpElementId = e.diagramId; mustReclick = true; /* sets the variables for the chain sound when dragging the element around */ elementPickedUp = true; lastDragPoint = evt.getPoint(); return; } } } } }else{ listener.executeCommand(HapticListenerCommand.MOVE, pickedUpElementId, evt.getX(), evt.getY(), startX, startY); elementPickedUp = false; secondClick = false; } mustReclick = true; } @Override public void mouseEntered(MouseEvent evt) {} @Override public void mouseExited(MouseEvent evt) {} @Override public void mouseClicked(MouseEvent evt) {} @Override public void mouseReleased(MouseEvent evt) {} private HapticListener listener; private JFrame hapticFrame; private HapticListSupport listSupport; private Stroke solidStroke; private Stroke dashedStroke; private Stroke dottedStroke; private Node lastTouchedNode; private Edge lastTouchedEdge; private Robot robot; private boolean mustReclick; private boolean reclickedAfterMove; private boolean secondClick; private boolean elementPickedUp; private Point2D lastDragPoint; private double draggedDistance; private int startX; private int startY; private int pickedUpElementId; private static final Color EDGE_COLOR = Color.RED; private static final Color NODE_COLOR = Color.WHITE; private static final int EDGE_THICKNESS = 26;//2; private static final int NODE_DIAMETER = 50;//10; private static final int NODE_HOVER_DIST = 25; private static final int EDGE_HOVER_DIST = 13; private static final double CHAIN_SOUND_INTERVAL = 150; }