Mercurial > hg > ccmieditor
view java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.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.speech; import java.awt.AWTKeyStroke; import java.awt.Component; import java.awt.Container; import java.awt.FocusTraversalPolicy; import java.awt.KeyboardFocusManager; import java.awt.event.ActionEvent; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ResourceBundle; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JSpinner; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.KeyStroke; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; /** * A class providing static utilities methods concerning the text to speech synthesis. * */ public abstract class SpeechUtilities { /* this class is of static use only */ private SpeechUtilities(){} public static String getComponentSpeech(Component c){ StringBuilder b = new StringBuilder(); if(c.getAccessibleContext().getAccessibleName() != null) b.append(c.getAccessibleContext().getAccessibleName()); if(c instanceof JButton) b.append(' ').append(resources.getString("component.button")); else if(c instanceof JTextField){ b.append(' ').append(resources.getString("component.text_field")); b.append(((JTextField)c).getText()); }else if(c instanceof JTextArea){ b.append(' ').append(resources.getString("component.text_area")); b.append(((JTextArea)c).getText()); }else if(c instanceof JComboBox){ b.append(((JComboBox)c).getSelectedItem().toString()); b.append(' ').append(resources.getString("component.combo_box")); }else if(c instanceof JCheckBox){ b.append(' ').append(((JCheckBox)c).isSelected() ? resources.getString("component.chech") : resources.getString("component.uncheck")); }else if(c instanceof JSpinner){ b.append(' ').append(resources.getString("component.spinner")); b.append(((JSpinner)c).getValue()); }else{ b.append(' ').append(c.getAccessibleContext().getAccessibleRole()); } return b.toString(); } @SuppressWarnings("serial") public static void changeTabListener(JComponent component, final Container container){ /* remove the default tab traversal key from all the containers */ disableTraversalKey(component); /* get the look and feel default keys for moving the focus on (usually = TAB) */ for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)) component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"tab"); /* add action to the moving focus keys: reproduce focus system and add speech to it */ component.getActionMap().put("tab", new AbstractAction(){ @Override public void actionPerformed(ActionEvent evt) { FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy(); Component next = policy.getComponentAfter(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner()); next.requestFocusInWindow(); NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(next)); } }); for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)) component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"back_tab"); component.getActionMap().put("back_tab", new AbstractAction(){ @Override public void actionPerformed(ActionEvent evt) { FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy(); Component previous = policy.getComponentBefore(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner()); previous.requestFocusInWindow(); NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(previous)); } }); } private static void disableTraversalKey(Container container){ for(final Component c : container.getComponents()){ if(c instanceof Container){ c.setFocusTraversalKeysEnabled(false); disableTraversalKey((Container)c); } } } public static KeyListener getSpeechKeyListener(boolean editableComponent){ if(!editableComponent) return new SpeechKeyListener(false); return speechKeyListener; } public static ItemListener getSpeechComboBoxItemListener(){ return comboBoxItemListener; } public static ItemListener getCheckBoxSpeechItemListener(){ return checkBoxItemListener; } public static FocusListener getFocusSpeechListener(){ return focusListener; } public static Action getShutUpAction(){ return shutUpAction; } /* * this class manages the speech feedback when moving around a text component * with the up, down, left and right arrows */ private static class SpeechKeyListener extends KeyAdapter{ boolean isTab; boolean isBeginning; boolean isFirstLine; boolean isLastLine; boolean editableComponent; SpeechKeyListener(boolean editablecomponent){ this.editableComponent = editablecomponent; } @Override public void keyTyped(KeyEvent evt){ /* this will manage digit or letter characters */ if(!isTab && !evt.isControlDown() && editableComponent){ if(Character.isLetterOrDigit(evt.getKeyChar())){ NarratorFactory.getInstance().speak(String.valueOf(evt.getKeyChar())); }else{ /* this will manage special characters with a letter representation */ switch(evt.getKeyChar()){ case '\n' : if(!(evt.getSource() instanceof JTextField)) NarratorFactory.getInstance().speak(resources.getString("char.new_line")); break; case ' ' : NarratorFactory.getInstance().speak(resources.getString("char.space")); break; case '@' : NarratorFactory.getInstance().speak(resources.getString("char.at")); break; case '.' : NarratorFactory.getInstance().speak(resources.getString("char.dot")); break; case ',' : NarratorFactory.getInstance().speak(resources.getString("char.comma")); break; case ';' : NarratorFactory.getInstance().speak(resources.getString("char.semi_colon")); break; case ':' : NarratorFactory.getInstance().speak(resources.getString("char.colon")); break; case '<' : NarratorFactory.getInstance().speak(resources.getString("char.lower_than")); break; case '>' : NarratorFactory.getInstance().speak(resources.getString("char.greater_than")); break; case '#' : NarratorFactory.getInstance().speak(resources.getString("char.sharp")); break; case '~' : NarratorFactory.getInstance().speak(resources.getString("char.tilde")); break; case '+' : NarratorFactory.getInstance().speak(resources.getString("char.plus")); break; case '-' : NarratorFactory.getInstance().speak(resources.getString("char.dash")); break; case '_' : NarratorFactory.getInstance().speak(resources.getString("char.underscore")); break; case '/' : NarratorFactory.getInstance().speak(resources.getString("char.slash")); break; } } } isTab = false; } /* manages all the non digit or letter characters */ @Override public void keyPressed(KeyEvent e){ int caretPos = ((JTextComponent)e.getSource()).getCaretPosition(); String text = ((JTextComponent)e.getSource()).getText(); if (e.getKeyCode() == KeyEvent.VK_TAB){ isTab = true; } if(caretPos == 0) isBeginning = true; else isBeginning = false; isFirstLine = true; for(int i=0; i<caretPos;i++){ if(text.charAt(i) == '\n'){ isFirstLine = false; break; } } if(text.indexOf('\n', caretPos) == -1) isLastLine = true; else isLastLine = false; } @Override public void keyReleased(KeyEvent evt){ JTextComponent textComponent = (JTextComponent)evt.getSource(); String text; int begin,end,caretPos; switch(evt.getKeyCode()){ case KeyEvent.VK_BACK_SPACE: NarratorFactory.getInstance().speak(resources.getString("char.back_space")); break; case KeyEvent.VK_DELETE : NarratorFactory.getInstance().speak(resources.getString("char.delete")); break; case KeyEvent.VK_LEFT : case KeyEvent.VK_RIGHT : try { if(evt.getKeyCode() == KeyEvent.VK_LEFT){ //left if(textComponent.getCaretPosition() == 0 && isBeginning){ SoundFactory.getInstance().play(SoundEvent.ERROR); return; } }else{ // right if(textComponent.getCaretPosition() == textComponent.getText().length()){ SoundFactory.getInstance().play(SoundEvent.ERROR); return; } } NarratorFactory.getInstance().speak(textComponent.getText(textComponent.getCaretPosition(),1)); } catch (BadLocationException e1) { e1.printStackTrace(); } break; case KeyEvent.VK_UP : /* when moving up and down, the line we land on is spoken out (the whole line). If the border * (top/bottom most line) is reached then the error sound is played. on a JTextField this * is the default behaviour with up and down keys as we only have one line */ if(isFirstLine){//we're on the first line and cannot go any upper SoundFactory.getInstance().play(SoundEvent.ERROR); return; } text = textComponent.getText(); caretPos = textComponent.getCaretPosition(); /* look for the beginning of the row the cursor is */ begin = 0; for(int i=0; i<caretPos;i++){ if(text.charAt(i) == '\n') begin = i+1; } /* now the end */ end = text.indexOf('\n', caretPos); if(end == begin)//in case it's an empty line end++; NarratorFactory.getInstance().speak(text.substring(begin, end)); break; case KeyEvent.VK_DOWN : if(isLastLine){ //no new line we either have one line only or sit on the last one SoundFactory.getInstance().play(SoundEvent.ERROR); return; } text = textComponent.getText(); caretPos = textComponent.getCaretPosition(); begin = 0; for(int i=0;i<caretPos;i++){ if(text.charAt(i) == '\n') begin = i+1; } begin = Math.min(begin, text.length()-1); end = text.indexOf('\n', begin); if(end == -1) // the line we're looking for is the last one end = text.length()-1; if(end == begin) // in case it's an empty line end++; NarratorFactory.getInstance().speak(text.substring(begin, end)); break; } } } private static final ResourceBundle resources = ResourceBundle.getBundle(Narrator.class.getName()); private static final SpeechKeyListener speechKeyListener = new SpeechKeyListener(true); private static final ItemListener comboBoxItemListener = new ItemListener(){ @Override public void itemStateChanged(ItemEvent evt) { if(evt.getStateChange() == ItemEvent.SELECTED) NarratorFactory.getInstance().speak(evt.getItem().toString()); } }; private static final ItemListener checkBoxItemListener = new ItemListener(){ @Override public void itemStateChanged(ItemEvent evt) { NarratorFactory.getInstance().speak(getComponentSpeech(((JCheckBox)evt.getItemSelectable()))); } }; private static final FocusListener focusListener = new FocusAdapter(){ public void focusGained(FocusEvent evt){ NarratorFactory.getInstance().speak(getComponentSpeech(evt.getComponent())); } }; @SuppressWarnings("serial") private static final Action shutUpAction = new AbstractAction(){ @Override public void actionPerformed(ActionEvent e) { NarratorFactory.getInstance().shutUp(); } }; }