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@0
|
23
|
fiore@0
|
24 import javax.swing.SwingUtilities;
|
fiore@0
|
25
|
fiore@0
|
26 import uk.ac.qmul.eecs.ccmi.diagrammodel.CollectionModel;
|
fiore@0
|
27 import uk.ac.qmul.eecs.ccmi.diagrammodel.DiagramElement;
|
fiore@0
|
28 import uk.ac.qmul.eecs.ccmi.haptics.HapticListener;
|
fiore@0
|
29 import uk.ac.qmul.eecs.ccmi.haptics.HapticListenerCommand;
|
fiore@0
|
30 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
|
fiore@0
|
31 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
|
fiore@0
|
32 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
|
fiore@0
|
33 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
|
fiore@0
|
34
|
fiore@0
|
35 /**
|
fiore@0
|
36 *
|
fiore@0
|
37 * An instance of HapticListener for the diagram editor. By this class visual diagrams
|
fiore@0
|
38 * can be manipulated by an haptic device.
|
fiore@0
|
39 *
|
fiore@0
|
40 */
|
fiore@0
|
41 public class HapticKindle extends HapticListener {
|
fiore@0
|
42
|
fiore@0
|
43 public HapticKindle(){
|
fiore@0
|
44 super();
|
fiore@0
|
45 unselectRunnable = new Runnable(){
|
fiore@0
|
46 @Override
|
fiore@0
|
47 public void run(){
|
fiore@0
|
48 frame.selectHapticHighligh(null);
|
fiore@0
|
49 }
|
fiore@0
|
50 };
|
fiore@0
|
51 frameBackupRunnable = new Runnable(){
|
fiore@0
|
52 @Override
|
fiore@0
|
53 public void run(){
|
fiore@0
|
54 frame.backupOpenDiagrams();
|
fiore@0
|
55 }
|
fiore@0
|
56 };
|
fiore@0
|
57 }
|
fiore@0
|
58
|
fiore@0
|
59 public void setEditorFrame(EditorFrame frame){
|
fiore@0
|
60 this.frame = frame;
|
fiore@0
|
61 }
|
fiore@0
|
62
|
fiore@0
|
63 @Override
|
fiore@0
|
64 public void executeCommand(HapticListenerCommand cmd, int ID, final double x, final double y, final double startX, final double startY) {
|
fiore@0
|
65 if((frame == null)||(frame.getActiveTab() == null))
|
fiore@0
|
66 return;
|
fiore@0
|
67 CollectionModel<Node,Edge> collectionModel = frame.getActiveTab().getDiagram().getCollectionModel();
|
fiore@0
|
68 Object monitor = collectionModel.getMonitor();
|
fiore@0
|
69 DiagramElement de = null;
|
fiore@0
|
70 switch(cmd){
|
fiore@0
|
71 case PLAY_ELEMENT_SOUND :
|
fiore@0
|
72 synchronized(monitor){
|
fiore@0
|
73 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
|
fiore@0
|
74 }
|
fiore@0
|
75 /* can be null if the tab has been switched or closed in the meantime */
|
fiore@0
|
76 if(de == null)
|
fiore@0
|
77 return;
|
fiore@0
|
78 SoundFactory.getInstance().play(de.getSound());
|
fiore@0
|
79 break;
|
fiore@0
|
80 case PLAY_ELEMENT_SPEECH :
|
fiore@0
|
81 synchronized(monitor){
|
fiore@0
|
82 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
|
fiore@0
|
83 }
|
fiore@0
|
84 if(de == null)
|
fiore@0
|
85 return;
|
fiore@0
|
86 SoundFactory.getInstance().play(de.getSound());
|
fiore@0
|
87 NarratorFactory.getInstance().speak(de.getName());
|
fiore@0
|
88 iLog("touch",((de instanceof Node) ? "node " : "edge ")+de.getName());
|
fiore@0
|
89 break;
|
fiore@0
|
90 case SELECT :
|
fiore@0
|
91 synchronized(monitor){
|
fiore@0
|
92 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
|
fiore@0
|
93 }
|
fiore@0
|
94 if(de == null)
|
fiore@0
|
95 return;
|
fiore@0
|
96 final DiagramElement selectedElement = de;
|
fiore@0
|
97 SwingUtilities.invokeLater(new Runnable(){
|
fiore@0
|
98 @Override
|
fiore@0
|
99 public void run(){
|
fiore@0
|
100 frame.selectHapticHighligh(selectedElement);
|
fiore@0
|
101 }
|
fiore@0
|
102 });
|
fiore@0
|
103 break;
|
fiore@0
|
104 case UNSELECT :
|
fiore@0
|
105 SwingUtilities.invokeLater(unselectRunnable);
|
fiore@0
|
106 break;
|
fiore@0
|
107 case MOVE :
|
fiore@0
|
108 DiagramPanel dPanel = frame.getActiveTab();
|
fiore@0
|
109 if(dPanel == null)
|
fiore@0
|
110 return;
|
fiore@0
|
111 synchronized(monitor){
|
fiore@0
|
112 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
|
fiore@0
|
113 }
|
fiore@0
|
114 if(de == null)
|
fiore@0
|
115 return;
|
fiore@0
|
116 final DiagramElement moveSelectedElement = de;
|
fiore@0
|
117 final DiagramModelUpdater modelUpdater = dPanel.getDiagram().getModelUpdater();
|
fiore@0
|
118 if(!modelUpdater.getLock(moveSelectedElement, Lock.MOVE)){
|
fiore@0
|
119 iLog("Could not get lock on element for motion", DiagramElement.toLogString(moveSelectedElement));
|
fiore@0
|
120 NarratorFactory.getInstance().speak("Object is being moved by another user");
|
fiore@0
|
121 return;
|
fiore@0
|
122 }
|
fiore@0
|
123
|
fiore@0
|
124 try {
|
fiore@0
|
125 SwingUtilities.invokeAndWait(new Runnable(){
|
fiore@0
|
126 @Override
|
fiore@0
|
127 public void run(){
|
fiore@0
|
128 if(moveSelectedElement instanceof Node){
|
fiore@0
|
129 Node n = (Node)moveSelectedElement;
|
fiore@0
|
130 Rectangle2D bounds = n.getBounds();
|
fiore@0
|
131 Point2D.Double p = new Point2D.Double(bounds.getCenterX(),bounds.getCenterY());
|
fiore@0
|
132 double dx = x - p.getX();
|
fiore@0
|
133 double dy = y - p.getY();
|
fiore@0
|
134 modelUpdater.translate(n, p, dx, dy);
|
fiore@0
|
135 modelUpdater.stopMove(n);
|
fiore@0
|
136
|
fiore@0
|
137 StringBuilder builder = new StringBuilder();
|
fiore@0
|
138 builder.append(DiagramElement.toLogString(n)).append(" ").append(p.getX())
|
fiore@0
|
139 .append(' ').append(p.getY());
|
fiore@0
|
140 iLog("move node start",builder.toString());
|
fiore@0
|
141 builder = new StringBuilder();
|
fiore@0
|
142 builder.append(DiagramElement.toLogString(n)).append(' ')
|
fiore@0
|
143 .append(x).append(' ').append(y);
|
fiore@0
|
144
|
fiore@0
|
145 iLog("move node end",builder.toString());
|
fiore@0
|
146
|
fiore@0
|
147 }else{
|
fiore@0
|
148 Edge e = (Edge)moveSelectedElement;
|
fiore@0
|
149 modelUpdater.startMove(e, new Point2D.Double(startX,startY));
|
fiore@0
|
150 Point2D p = new Point2D.Double(x,y);
|
fiore@0
|
151 modelUpdater.bend(e, p);
|
fiore@0
|
152 modelUpdater.stopMove(e);
|
fiore@0
|
153
|
fiore@0
|
154 StringBuilder builder = new StringBuilder();
|
fiore@0
|
155 builder.append(DiagramElement.toLogString(e)).append(' ').append(startX)
|
fiore@0
|
156 .append(' ').append(startY);
|
fiore@0
|
157 iLog("bend edge start",builder.toString());
|
fiore@0
|
158 builder = new StringBuilder();
|
fiore@0
|
159 builder.append(DiagramElement.toLogString(e)).append(' ')
|
fiore@0
|
160 .append(x).append(' ').append(y);
|
fiore@0
|
161 iLog("bend edge end",builder.toString());
|
fiore@0
|
162
|
fiore@0
|
163 }
|
fiore@0
|
164 }
|
fiore@0
|
165 });
|
fiore@0
|
166 } catch (Exception e) {
|
fiore@0
|
167 throw new RuntimeException(e);
|
fiore@0
|
168 }
|
fiore@0
|
169 SoundFactory.getInstance().play(SoundEvent.HOOK_OFF);
|
fiore@0
|
170 modelUpdater.yieldLock(moveSelectedElement, Lock.MOVE);
|
fiore@0
|
171 break;
|
fiore@0
|
172 case INFO :
|
fiore@0
|
173 synchronized(monitor){
|
fiore@0
|
174 de = Finder.findElementByHashcode(ID, collectionModel.getElements());
|
fiore@0
|
175 }
|
fiore@0
|
176 if(de == null)
|
fiore@0
|
177 return;
|
fiore@0
|
178 SoundFactory.getInstance().stop();
|
fiore@0
|
179 NarratorFactory.getInstance().speak(de.detailedSpokenText());
|
fiore@0
|
180 iLog("request detailed info",((de instanceof Node) ? "node " : "edge ")+de.getName());
|
fiore@0
|
181 break;
|
fiore@0
|
182 case PLAY_SOUND :
|
fiore@0
|
183 switch(HapticListenerCommand.Sound.fromInt(ID) ){
|
fiore@0
|
184 case MAGNET_OFF :
|
fiore@0
|
185 SoundFactory.getInstance().play(SoundEvent.MAGNET_OFF);
|
fiore@0
|
186 iLog("sticky mode off","");
|
fiore@0
|
187 break;
|
fiore@0
|
188 case MAGNET_ON :
|
fiore@0
|
189 SoundFactory.getInstance().play(SoundEvent.MAGNET_ON);
|
fiore@0
|
190 iLog("sticky mode on","");
|
fiore@0
|
191 break;
|
fiore@0
|
192 case HOOK_ON :
|
fiore@0
|
193 SoundFactory.getInstance().play(SoundEvent.HOOK_ON);
|
fiore@0
|
194 iLog("hook on","");
|
fiore@0
|
195 break;
|
fiore@0
|
196 case DRAG : SoundFactory.getInstance().play(SoundEvent.DRAG);
|
fiore@0
|
197 break;
|
fiore@0
|
198 }
|
fiore@0
|
199 break;
|
fiore@0
|
200 case ERROR :
|
fiore@0
|
201 /* no synchronization necessary as the XMLManager looks after it*/
|
fiore@0
|
202 SwingUtilities.invokeLater(frameBackupRunnable);
|
fiore@0
|
203 NarratorFactory.getInstance().speak("Haptic device crashed. " +
|
fiore@0
|
204 "Unsaved diagrams saved in backup directory");
|
fiore@0
|
205 break;
|
fiore@0
|
206 }
|
fiore@0
|
207 }
|
fiore@0
|
208
|
fiore@0
|
209 private void iLog(String action, String args){
|
fiore@0
|
210 InteractionLog.log(INTERACTION_LOG_SOURCE,action,args);
|
fiore@0
|
211 }
|
fiore@0
|
212
|
fiore@0
|
213 private Runnable unselectRunnable;
|
fiore@0
|
214 private Runnable frameBackupRunnable;
|
fiore@0
|
215 private static String INTERACTION_LOG_SOURCE = "HAPTIC";
|
fiore@0
|
216 private EditorFrame frame;
|
fiore@0
|
217 }
|