annotate java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.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
rev   line source
f@0 1 /*
f@0 2 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
f@0 3
f@0 4 Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
f@0 5
f@0 6 This program is free software: you can redistribute it and/or modify
f@0 7 it under the terms of the GNU General Public License as published by
f@0 8 the Free Software Foundation, either version 3 of the License, or
f@0 9 (at your option) any later version.
f@0 10
f@0 11 This program is distributed in the hope that it will be useful,
f@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 14 GNU General Public License for more details.
f@0 15
f@0 16 You should have received a copy of the GNU General Public License
f@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 18 */
f@0 19
f@0 20 package uk.ac.qmul.eecs.ccmi.speech;
f@0 21
f@0 22 import java.awt.AWTKeyStroke;
f@0 23 import java.awt.Component;
f@0 24 import java.awt.Container;
f@0 25 import java.awt.FocusTraversalPolicy;
f@0 26 import java.awt.KeyboardFocusManager;
f@0 27 import java.awt.event.ActionEvent;
f@0 28 import java.awt.event.ItemEvent;
f@0 29 import java.awt.event.ItemListener;
f@0 30 import java.awt.event.KeyAdapter;
f@0 31 import java.awt.event.KeyEvent;
f@0 32 import java.awt.event.KeyListener;
f@0 33 import java.util.ResourceBundle;
f@0 34
f@0 35 import javax.swing.AbstractAction;
f@0 36 import javax.swing.Action;
f@0 37 import javax.swing.JButton;
f@0 38 import javax.swing.JCheckBox;
f@0 39 import javax.swing.JComboBox;
f@0 40 import javax.swing.JComponent;
f@0 41 import javax.swing.JSpinner;
f@0 42 import javax.swing.JTabbedPane;
f@0 43 import javax.swing.JTextArea;
f@0 44 import javax.swing.JTextField;
f@0 45 import javax.swing.JTree;
f@0 46 import javax.swing.KeyStroke;
f@0 47 import javax.swing.text.BadLocationException;
f@0 48 import javax.swing.text.JTextComponent;
f@0 49
f@0 50 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
f@0 51 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
f@0 52 import uk.ac.qmul.eecs.ccmi.utils.InteractionLog;
f@0 53
f@0 54 /**
f@0 55 * A class providing static utilities methods concerning the text to speech synthesis.
f@0 56 *
f@0 57 */
f@0 58 public abstract class SpeechUtilities {
f@0 59 /* this class is of static use only */
f@0 60 private SpeechUtilities(){}
f@0 61
f@0 62 public static String getComponentSpeech(Component c){
f@0 63 StringBuilder b = new StringBuilder();
f@0 64 if(c.getAccessibleContext().getAccessibleName() != null)
f@0 65 b.append(c.getAccessibleContext().getAccessibleName());
f@0 66 if(c instanceof JButton)
f@0 67 b.append(' ').append(resources.getString("component.button"));
f@0 68 else if(c instanceof JTextField){
f@0 69 b.append(' ').append(resources.getString("component.text_field"));
f@0 70 b.append(((JTextField)c).getText());
f@0 71 }else if(c instanceof JTextArea){
f@0 72 b.append(' ').append(resources.getString("component.text_area"));
f@0 73 b.append(((JTextArea)c).getText());
f@0 74 }else if(c instanceof JComboBox){
f@0 75 b.append(((JComboBox)c).getSelectedItem().toString());
f@0 76 b.append(' ').append(resources.getString("component.combo_box"));
f@0 77 }else if(c instanceof JCheckBox){
f@0 78 b.append(' ').append(((JCheckBox)c).isSelected() ? resources.getString("component.chech") : resources.getString("component.uncheck"));
f@0 79 }else if(c instanceof JSpinner){
f@0 80 b.append(' ').append(resources.getString("component.spinner"));
f@0 81 b.append(((JSpinner)c).getValue());
f@0 82 }else if(c instanceof JTabbedPane){
f@0 83 Component comp = ((JTabbedPane)c).getSelectedComponent();
f@0 84 if(comp == null)
f@0 85 return "";
f@0 86 b.append(' ').append( comp.getName());
f@0 87 }else if(!(c instanceof JTree)){
f@0 88 b.append(' ').append(c.getAccessibleContext().getAccessibleRole());
f@0 89 }
f@0 90 return b.toString();
f@0 91 }
f@0 92
f@0 93 @SuppressWarnings("serial")
f@0 94 public static void changeTabListener(JComponent component, final Container container){
f@0 95 /* remove the default tab traversal key from all the containers */
f@0 96 disableTraversalKey(component);
f@0 97 /* get the look and feel default keys for moving the focus on (usually = TAB) */
f@0 98 for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS))
f@0 99 component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"tab");
f@0 100
f@0 101 /* add action to the moving focus keys: reproduce focus system and add speech to it */
f@0 102 component.getActionMap().put("tab", new AbstractAction(){
f@0 103 @Override
f@0 104 public void actionPerformed(ActionEvent evt) {
f@0 105 FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy();
f@0 106 Component next = policy.getComponentAfter(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
f@0 107 if(next == null)
f@0 108 return;
f@0 109 next.requestFocusInWindow();
f@0 110 NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(next));
f@0 111 InteractionLog.log("TABBED PANE","change focus ",next.getAccessibleContext().getAccessibleName());
f@0 112 }
f@0 113 });
f@0 114
f@0 115 for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS))
f@0 116 component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"back_tab");
f@0 117
f@0 118 component.getActionMap().put("back_tab", new AbstractAction(){
f@0 119 @Override
f@0 120 public void actionPerformed(ActionEvent evt) {
f@0 121 FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy();
f@0 122 Component previous = policy.getComponentBefore(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner());
f@0 123 if(previous == null)
f@0 124 return;
f@0 125 previous.requestFocusInWindow();
f@0 126 NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(previous));
f@0 127 InteractionLog.log("TABBED PANE","change focus ",previous.getAccessibleContext().getAccessibleName());
f@0 128 }
f@0 129 });
f@0 130
f@0 131 /* shut up the narrator upon pressing ctrl */
f@0 132 // component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL,InputEvent.CTRL_DOWN_MASK),"ctrldown");
f@0 133 // component.getActionMap().put("ctrldown",new AbstractAction(){
f@0 134 // public void actionPerformed(ActionEvent evt){
f@0 135 // NarratorFactory.getInstance().shutUp();
f@0 136 // }
f@0 137 // });
f@0 138 }
f@0 139
f@0 140 private static void disableTraversalKey(Container container){
f@0 141 for(final Component c : container.getComponents()){
f@0 142 if(c instanceof Container){
f@0 143 c.setFocusTraversalKeysEnabled(false);
f@0 144 disableTraversalKey((Container)c);
f@0 145 }
f@0 146 }
f@0 147 }
f@0 148
f@0 149 public static KeyListener getSpeechKeyListener(boolean editableComponent, boolean secondVoice){
f@0 150 if(!editableComponent)
f@0 151 return new SpeechKeyListener(false,secondVoice);
f@0 152 return speechKeyListener;
f@0 153 }
f@0 154
f@0 155 /**
f@0 156 * Returns a {@code speechKeyListener} using first voice (default)
f@0 157 * @param editableComponent whether this key listener is for a component
f@0 158 * that will be editable
f@0 159 * @return a key listener that utters the letters when typed
f@0 160 */
f@0 161 public static KeyListener getSpeechKeyListener(boolean editableComponent){
f@0 162 return getSpeechKeyListener(editableComponent,false);
f@0 163 }
f@0 164
f@0 165 public static ItemListener getSpeechComboBoxItemListener(){
f@0 166 return comboBoxItemListener;
f@0 167 }
f@0 168
f@0 169 public static ItemListener getCheckBoxSpeechItemListener(){
f@0 170 return checkBoxItemListener;
f@0 171 }
f@0 172
f@0 173 public static Action getShutUpAction(){
f@0 174 return shutUpAction;
f@0 175 }
f@0 176
f@0 177 /*
f@0 178 * this class manages the speech feedback when moving around a text component
f@0 179 * with the up, down, left and right arrows
f@0 180 */
f@0 181 private static class SpeechKeyListener extends KeyAdapter{
f@0 182 boolean isTab;
f@0 183 boolean isBeginning;
f@0 184 boolean isFirstLine;
f@0 185 boolean isLastLine;
f@0 186 boolean editableComponent;
f@0 187 int voice;
f@0 188
f@0 189 SpeechKeyListener(boolean editablecomponent, boolean useSecondVoice){
f@0 190 this.editableComponent = editablecomponent;
f@0 191 voice = useSecondVoice ? Narrator.SECOND_VOICE : Narrator.FIRST_VOICE;
f@0 192 }
f@0 193
f@0 194 @Override
f@0 195 public void keyTyped(KeyEvent evt){
f@0 196 /* this will manage digit or letter characters */
f@0 197 if(!isTab && !evt.isControlDown() && editableComponent){
f@0 198 if(Character.isLetterOrDigit(evt.getKeyChar())){
f@0 199 NarratorFactory.getInstance().speak(String.valueOf(evt.getKeyChar()),voice);
f@0 200 }else{
f@0 201 /* this will manage special characters with a letter representation */
f@0 202 switch(evt.getKeyChar()){
f@0 203 case '\n' :
f@0 204 if(!(evt.getSource() instanceof JTextField))
f@0 205 NarratorFactory.getInstance().speak(resources.getString("char.new_line"),voice);
f@0 206 break;
f@0 207 case ' ' :
f@0 208 NarratorFactory.getInstance().speak(resources.getString("char.space"),voice);
f@0 209 break;
f@0 210 case '@' :
f@0 211 NarratorFactory.getInstance().speak(resources.getString("char.at"),voice);
f@0 212 break;
f@0 213 case '*' :
f@0 214 NarratorFactory.getInstance().speak(resources.getString("char.asterisk"),voice);
f@0 215 break;
f@0 216 case '$' :
f@0 217 NarratorFactory.getInstance().speak(resources.getString("char.dollar"),voice);
f@0 218 break;
f@0 219 case '.' :
f@0 220 NarratorFactory.getInstance().speak(resources.getString("char.dot"),voice);
f@0 221 break;
f@0 222 case ',' :
f@0 223 NarratorFactory.getInstance().speak(resources.getString("char.comma"),voice);
f@0 224 break;
f@0 225 case ';' :
f@0 226 NarratorFactory.getInstance().speak(resources.getString("char.semi_colon"),voice);
f@0 227 break;
f@0 228 case ':' :
f@0 229 NarratorFactory.getInstance().speak(resources.getString("char.colon"),voice);
f@0 230 break;
f@0 231 case '<' :
f@0 232 NarratorFactory.getInstance().speak(resources.getString("char.lower_than"),voice);
f@0 233 break;
f@0 234 case '>' :
f@0 235 NarratorFactory.getInstance().speak(resources.getString("char.greater_than"),voice);
f@0 236 break;
f@0 237 case '#' :
f@0 238 NarratorFactory.getInstance().speak(resources.getString("char.sharp"),voice);
f@0 239 break;
f@0 240 case '~' :
f@0 241 NarratorFactory.getInstance().speak(resources.getString("char.tilde"),voice);
f@0 242 break;
f@0 243 case '+' :
f@0 244 NarratorFactory.getInstance().speak(resources.getString("char.plus"),voice);
f@0 245 break;
f@0 246 case '-' :
f@0 247 NarratorFactory.getInstance().speak(resources.getString("char.dash"),voice);
f@0 248 break;
f@0 249 case '_' :
f@0 250 NarratorFactory.getInstance().speak(resources.getString("char.underscore"),voice);
f@0 251 break;
f@0 252 case '/' :
f@0 253 NarratorFactory.getInstance().speak(resources.getString("char.slash"),voice);
f@0 254 break;
f@0 255 }
f@0 256 }
f@0 257 }
f@0 258 isTab = false;
f@0 259 }
f@0 260
f@0 261 /* manages all the non digit or letter characters */
f@0 262 @Override
f@0 263 public void keyPressed(KeyEvent e){
f@0 264 int caretPos = ((JTextComponent)e.getSource()).getCaretPosition();
f@0 265 String text = ((JTextComponent)e.getSource()).getText();
f@0 266
f@0 267 if (e.getKeyCode() == KeyEvent.VK_TAB){
f@0 268 isTab = true;
f@0 269 }
f@0 270 if(caretPos == 0)
f@0 271 isBeginning = true;
f@0 272 else
f@0 273 isBeginning = false;
f@0 274
f@0 275 isFirstLine = true;
f@0 276 for(int i=0; i<caretPos;i++){
f@0 277 if(text.charAt(i) == '\n'){
f@0 278 isFirstLine = false;
f@0 279 break;
f@0 280 }
f@0 281 }
f@0 282
f@0 283 if(text.indexOf('\n', caretPos) == -1)
f@0 284 isLastLine = true;
f@0 285 else
f@0 286 isLastLine = false;
f@0 287 }
f@0 288
f@0 289 @Override
f@0 290 public void keyReleased(KeyEvent evt){
f@0 291 JTextComponent textComponent = (JTextComponent)evt.getSource();
f@0 292 String text;
f@0 293 int begin,end,caretPos;
f@0 294
f@0 295 switch(evt.getKeyCode()){
f@0 296 case KeyEvent.VK_BACK_SPACE:
f@0 297 NarratorFactory.getInstance().speak(resources.getString("char.back_space"),voice);
f@0 298 break;
f@0 299 case KeyEvent.VK_DELETE :
f@0 300 NarratorFactory.getInstance().speak(resources.getString("char.delete"),voice);
f@0 301 break;
f@0 302 case KeyEvent.VK_LEFT :
f@0 303 case KeyEvent.VK_RIGHT :
f@0 304 try {
f@0 305 if(evt.getKeyCode() == KeyEvent.VK_LEFT){ //left
f@0 306 if(textComponent.getCaretPosition() == 0 && isBeginning){
f@0 307 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 308 return;
f@0 309 }
f@0 310 }else{ // right
f@0 311 if(textComponent.getCaretPosition() == textComponent.getText().length()){
f@0 312 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 313 return;
f@0 314 }
f@0 315 }
f@0 316 NarratorFactory.getInstance().speak(textComponent.getText(textComponent.getCaretPosition(),1),voice);
f@0 317 } catch (BadLocationException e1) {
f@0 318 e1.printStackTrace();
f@0 319 }
f@0 320 break;
f@0 321 case KeyEvent.VK_UP :
f@0 322 /* when moving up and down, the line we land on is spoken out (the whole line). If the border
f@0 323 * (top/bottom most line) is reached then the error sound is played. on a JTextField this
f@0 324 * is the default behaviour with up and down keys as we only have one line
f@0 325 */
f@0 326
f@0 327 if(isFirstLine){//we're on the first line and cannot go any upper
f@0 328 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 329 return;
f@0 330 }
f@0 331
f@0 332 text = textComponent.getText();
f@0 333 caretPos = textComponent.getCaretPosition();
f@0 334
f@0 335 /* look for the beginning of the row the cursor is */
f@0 336 begin = 0;
f@0 337 for(int i=0; i<caretPos;i++){
f@0 338 if(text.charAt(i) == '\n')
f@0 339 begin = i+1;
f@0 340 }
f@0 341
f@0 342 /* now the end */
f@0 343 end = text.indexOf('\n', caretPos);
f@0 344
f@0 345 if(end == begin)//in case it's an empty line
f@0 346 end++;
f@0 347 NarratorFactory.getInstance().speak(text.substring(begin, end),voice);
f@0 348 break;
f@0 349 case KeyEvent.VK_DOWN :
f@0 350 if(isLastLine){ //no new line we either have one line only or sit on the last one
f@0 351 SoundFactory.getInstance().play(SoundEvent.ERROR);
f@0 352 return;
f@0 353 }
f@0 354
f@0 355 text = textComponent.getText();
f@0 356 caretPos = textComponent.getCaretPosition();
f@0 357
f@0 358 begin = 0;
f@0 359 for(int i=0;i<caretPos;i++){
f@0 360 if(text.charAt(i) == '\n')
f@0 361 begin = i+1;
f@0 362 }
f@0 363 begin = Math.min(begin, text.length()-1);
f@0 364
f@0 365 end = text.indexOf('\n', begin);
f@0 366 if(end == -1) // the line we're looking for is the last one
f@0 367 end = text.length()-1;
f@0 368
f@0 369 if(end == begin) // in case it's an empty line
f@0 370 end++;
f@0 371 NarratorFactory.getInstance().speak(text.substring(begin, end),voice);
f@0 372 break;
f@0 373 }
f@0 374 }
f@0 375 }
f@0 376
f@0 377 private static final ResourceBundle resources = ResourceBundle.getBundle(Narrator.class.getName());
f@0 378 private static final SpeechKeyListener speechKeyListener = new SpeechKeyListener(true,false);
f@0 379 private static final ItemListener comboBoxItemListener = new ItemListener(){
f@0 380 @Override
f@0 381 public void itemStateChanged(ItemEvent evt) {
f@0 382 if(evt.getStateChange() == ItemEvent.SELECTED)
f@0 383 NarratorFactory.getInstance().speak(evt.getItem().toString());
f@0 384 }
f@0 385 };
f@0 386
f@0 387 private static final ItemListener checkBoxItemListener = new ItemListener(){
f@0 388 @Override
f@0 389 public void itemStateChanged(ItemEvent evt) {
f@0 390 NarratorFactory.getInstance().speak(getComponentSpeech(((JCheckBox)evt.getItemSelectable())));
f@0 391 }
f@0 392 };
f@0 393
f@0 394 @SuppressWarnings("serial")
f@0 395 private static final Action shutUpAction = new AbstractAction(){
f@0 396 @Override
f@0 397 public void actionPerformed(ActionEvent e) {
f@0 398 NarratorFactory.getInstance().shutUp();
f@0 399 }
f@0 400 };
f@0 401 }