Mercurial > hg > ccmieditor
view java/src/uk/ac/qmul/eecs/ccmi/speech/SpeechUtilities.java @ 8:ea7885bd9bff tip
fixed bug : render solid line as dotted/dashed when moving the stylus from dotted/dashed to solid
author | ccmi-guest |
---|---|
date | Thu, 03 Jul 2014 16:12:20 +0100 |
parents | 075ae9eb2a40 |
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.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.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.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JTree; 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; import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; /** * 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 if(c instanceof JTabbedPane){ Component comp = ((JTabbedPane)c).getSelectedComponent(); if(comp == null) return ""; b.append(' ').append( comp.getName()); }else if(!(c instanceof JTree)){ 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()); if(next == null) return; next.requestFocusInWindow(); NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(next)); InteractionLog.log("TABBED PANE","change focus ",next.getAccessibleContext().getAccessibleName()); } }); 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()); if(previous == null) return; previous.requestFocusInWindow(); NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(previous)); InteractionLog.log("TABBED PANE","change focus ",previous.getAccessibleContext().getAccessibleName()); } }); /* shut up the narrator upon pressing ctrl */ // component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL,InputEvent.CTRL_DOWN_MASK),"ctrldown"); // component.getActionMap().put("ctrldown",new AbstractAction(){ // public void actionPerformed(ActionEvent evt){ // NarratorFactory.getInstance().shutUp(); // } // }); } 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, boolean secondVoice){ if(!editableComponent) return new SpeechKeyListener(false,secondVoice); return speechKeyListener; } /** * Returns a {@code speechKeyListener} using first voice (default) * @param editableComponent whether this key listener is for a component * that will be editable * @return a key listener that utters the letters when typed */ public static KeyListener getSpeechKeyListener(boolean editableComponent){ return getSpeechKeyListener(editableComponent,false); } public static ItemListener getSpeechComboBoxItemListener(){ return comboBoxItemListener; } public static ItemListener getCheckBoxSpeechItemListener(){ return checkBoxItemListener; } 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; int voice; SpeechKeyListener(boolean editablecomponent, boolean useSecondVoice){ this.editableComponent = editablecomponent; voice = useSecondVoice ? Narrator.SECOND_VOICE : Narrator.FIRST_VOICE; } @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()),voice); }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"),voice); break; case ' ' : NarratorFactory.getInstance().speak(resources.getString("char.space"),voice); break; case '@' : NarratorFactory.getInstance().speak(resources.getString("char.at"),voice); break; case '*' : NarratorFactory.getInstance().speak(resources.getString("char.asterisk"),voice); break; case '$' : NarratorFactory.getInstance().speak(resources.getString("char.dollar"),voice); break; case '.' : NarratorFactory.getInstance().speak(resources.getString("char.dot"),voice); break; case ',' : NarratorFactory.getInstance().speak(resources.getString("char.comma"),voice); break; case ';' : NarratorFactory.getInstance().speak(resources.getString("char.semi_colon"),voice); break; case ':' : NarratorFactory.getInstance().speak(resources.getString("char.colon"),voice); break; case '<' : NarratorFactory.getInstance().speak(resources.getString("char.lower_than"),voice); break; case '>' : NarratorFactory.getInstance().speak(resources.getString("char.greater_than"),voice); break; case '#' : NarratorFactory.getInstance().speak(resources.getString("char.sharp"),voice); break; case '~' : NarratorFactory.getInstance().speak(resources.getString("char.tilde"),voice); break; case '+' : NarratorFactory.getInstance().speak(resources.getString("char.plus"),voice); break; case '-' : NarratorFactory.getInstance().speak(resources.getString("char.dash"),voice); break; case '_' : NarratorFactory.getInstance().speak(resources.getString("char.underscore"),voice); break; case '/' : NarratorFactory.getInstance().speak(resources.getString("char.slash"),voice); 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"),voice); break; case KeyEvent.VK_DELETE : NarratorFactory.getInstance().speak(resources.getString("char.delete"),voice); 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),voice); } 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),voice); 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),voice); break; } } } private static final ResourceBundle resources = ResourceBundle.getBundle(Narrator.class.getName()); private static final SpeechKeyListener speechKeyListener = new SpeechKeyListener(true,false); 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()))); } }; @SuppressWarnings("serial") private static final Action shutUpAction = new AbstractAction(){ @Override public void actionPerformed(ActionEvent e) { NarratorFactory.getInstance().shutUp(); } }; }