fiore@0: /* fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool fiore@0: fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) fiore@0: fiore@0: This program is free software: you can redistribute it and/or modify fiore@0: it under the terms of the GNU General Public License as published by fiore@0: the Free Software Foundation, either version 3 of the License, or fiore@0: (at your option) any later version. fiore@0: fiore@0: This program is distributed in the hope that it will be useful, fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fiore@0: GNU General Public License for more details. fiore@0: fiore@0: You should have received a copy of the GNU General Public License fiore@0: along with this program. If not, see . fiore@0: */ fiore@0: fiore@0: package uk.ac.qmul.eecs.ccmi.speech; fiore@0: fiore@0: import java.awt.AWTKeyStroke; fiore@0: import java.awt.Component; fiore@0: import java.awt.Container; fiore@0: import java.awt.FocusTraversalPolicy; fiore@0: import java.awt.KeyboardFocusManager; fiore@0: import java.awt.event.ActionEvent; fiore@0: import java.awt.event.ItemEvent; fiore@0: import java.awt.event.ItemListener; fiore@0: import java.awt.event.KeyAdapter; fiore@0: import java.awt.event.KeyEvent; fiore@0: import java.awt.event.KeyListener; fiore@0: import java.util.ResourceBundle; fiore@0: fiore@0: import javax.swing.AbstractAction; fiore@0: import javax.swing.Action; fiore@0: import javax.swing.JButton; fiore@0: import javax.swing.JCheckBox; fiore@0: import javax.swing.JComboBox; fiore@0: import javax.swing.JComponent; fiore@0: import javax.swing.JSpinner; fiore@3: import javax.swing.JTabbedPane; fiore@0: import javax.swing.JTextArea; fiore@0: import javax.swing.JTextField; fiore@3: import javax.swing.JTree; fiore@0: import javax.swing.KeyStroke; fiore@0: import javax.swing.text.BadLocationException; fiore@0: import javax.swing.text.JTextComponent; fiore@0: fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; fiore@3: import uk.ac.qmul.eecs.ccmi.utils.InteractionLog; fiore@0: fiore@0: /** fiore@0: * A class providing static utilities methods concerning the text to speech synthesis. fiore@0: * fiore@0: */ fiore@0: public abstract class SpeechUtilities { fiore@0: /* this class is of static use only */ fiore@0: private SpeechUtilities(){} fiore@0: fiore@0: public static String getComponentSpeech(Component c){ fiore@0: StringBuilder b = new StringBuilder(); fiore@0: if(c.getAccessibleContext().getAccessibleName() != null) fiore@0: b.append(c.getAccessibleContext().getAccessibleName()); fiore@0: if(c instanceof JButton) fiore@0: b.append(' ').append(resources.getString("component.button")); fiore@0: else if(c instanceof JTextField){ fiore@0: b.append(' ').append(resources.getString("component.text_field")); fiore@0: b.append(((JTextField)c).getText()); fiore@0: }else if(c instanceof JTextArea){ fiore@0: b.append(' ').append(resources.getString("component.text_area")); fiore@0: b.append(((JTextArea)c).getText()); fiore@0: }else if(c instanceof JComboBox){ fiore@0: b.append(((JComboBox)c).getSelectedItem().toString()); fiore@0: b.append(' ').append(resources.getString("component.combo_box")); fiore@0: }else if(c instanceof JCheckBox){ fiore@0: b.append(' ').append(((JCheckBox)c).isSelected() ? resources.getString("component.chech") : resources.getString("component.uncheck")); fiore@0: }else if(c instanceof JSpinner){ fiore@0: b.append(' ').append(resources.getString("component.spinner")); fiore@0: b.append(((JSpinner)c).getValue()); fiore@3: }else if(c instanceof JTabbedPane){ fiore@3: Component comp = ((JTabbedPane)c).getSelectedComponent(); fiore@3: if(comp == null) fiore@3: return ""; fiore@3: b.append(' ').append( comp.getName()); fiore@3: }else if(!(c instanceof JTree)){ fiore@0: b.append(' ').append(c.getAccessibleContext().getAccessibleRole()); fiore@0: } fiore@0: return b.toString(); fiore@0: } fiore@0: fiore@0: @SuppressWarnings("serial") fiore@0: public static void changeTabListener(JComponent component, final Container container){ fiore@0: /* remove the default tab traversal key from all the containers */ fiore@0: disableTraversalKey(component); fiore@0: /* get the look and feel default keys for moving the focus on (usually = TAB) */ fiore@0: for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)) fiore@0: component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"tab"); fiore@0: fiore@0: /* add action to the moving focus keys: reproduce focus system and add speech to it */ fiore@0: component.getActionMap().put("tab", new AbstractAction(){ fiore@0: @Override fiore@0: public void actionPerformed(ActionEvent evt) { fiore@0: FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy(); fiore@0: Component next = policy.getComponentAfter(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner()); fiore@7: if(next == null) fiore@7: return; fiore@0: next.requestFocusInWindow(); fiore@0: NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(next)); fiore@3: InteractionLog.log("TABBED PANE","change focus ",next.getAccessibleContext().getAccessibleName()); fiore@0: } fiore@0: }); fiore@0: fiore@0: for(AWTKeyStroke keyStroke : KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)) fiore@0: component.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(keyStroke.getKeyCode(),keyStroke.getModifiers()),"back_tab"); fiore@0: fiore@0: component.getActionMap().put("back_tab", new AbstractAction(){ fiore@0: @Override fiore@0: public void actionPerformed(ActionEvent evt) { fiore@0: FocusTraversalPolicy policy = KeyboardFocusManager.getCurrentKeyboardFocusManager().getDefaultFocusTraversalPolicy(); fiore@0: Component previous = policy.getComponentBefore(container, KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner()); fiore@7: if(previous == null) fiore@7: return; fiore@0: previous.requestFocusInWindow(); fiore@0: NarratorFactory.getInstance().speak(SpeechUtilities.getComponentSpeech(previous)); fiore@3: InteractionLog.log("TABBED PANE","change focus ",previous.getAccessibleContext().getAccessibleName()); fiore@0: } fiore@0: }); fiore@3: fiore@3: /* shut up the narrator upon pressing ctrl */ fiore@3: // component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL,InputEvent.CTRL_DOWN_MASK),"ctrldown"); fiore@3: // component.getActionMap().put("ctrldown",new AbstractAction(){ fiore@3: // public void actionPerformed(ActionEvent evt){ fiore@3: // NarratorFactory.getInstance().shutUp(); fiore@3: // } fiore@3: // }); fiore@0: } fiore@0: fiore@0: private static void disableTraversalKey(Container container){ fiore@0: for(final Component c : container.getComponents()){ fiore@0: if(c instanceof Container){ fiore@0: c.setFocusTraversalKeysEnabled(false); fiore@0: disableTraversalKey((Container)c); fiore@0: } fiore@0: } fiore@0: } fiore@0: fiore@3: public static KeyListener getSpeechKeyListener(boolean editableComponent, boolean secondVoice){ fiore@3: if(!editableComponent) fiore@3: return new SpeechKeyListener(false,secondVoice); fiore@3: return speechKeyListener; fiore@3: } fiore@3: fiore@3: /** fiore@3: * Returns a {@code speechKeyListener} using first voice (default) fiore@5: * @param editableComponent whether this key listener is for a component fiore@5: * that will be editable fiore@5: * @return a key listener that utters the letters when typed fiore@3: */ fiore@0: public static KeyListener getSpeechKeyListener(boolean editableComponent){ fiore@3: return getSpeechKeyListener(editableComponent,false); fiore@0: } fiore@0: fiore@0: public static ItemListener getSpeechComboBoxItemListener(){ fiore@0: return comboBoxItemListener; fiore@0: } fiore@0: fiore@0: public static ItemListener getCheckBoxSpeechItemListener(){ fiore@0: return checkBoxItemListener; fiore@0: } fiore@0: fiore@0: public static Action getShutUpAction(){ fiore@0: return shutUpAction; fiore@0: } fiore@0: fiore@0: /* fiore@0: * this class manages the speech feedback when moving around a text component fiore@0: * with the up, down, left and right arrows fiore@0: */ fiore@0: private static class SpeechKeyListener extends KeyAdapter{ fiore@0: boolean isTab; fiore@0: boolean isBeginning; fiore@0: boolean isFirstLine; fiore@0: boolean isLastLine; fiore@0: boolean editableComponent; fiore@3: int voice; fiore@0: fiore@3: SpeechKeyListener(boolean editablecomponent, boolean useSecondVoice){ fiore@0: this.editableComponent = editablecomponent; fiore@3: voice = useSecondVoice ? Narrator.SECOND_VOICE : Narrator.FIRST_VOICE; fiore@0: } fiore@0: fiore@0: @Override fiore@0: public void keyTyped(KeyEvent evt){ fiore@0: /* this will manage digit or letter characters */ fiore@0: if(!isTab && !evt.isControlDown() && editableComponent){ fiore@0: if(Character.isLetterOrDigit(evt.getKeyChar())){ fiore@3: NarratorFactory.getInstance().speak(String.valueOf(evt.getKeyChar()),voice); fiore@0: }else{ fiore@0: /* this will manage special characters with a letter representation */ fiore@0: switch(evt.getKeyChar()){ fiore@0: case '\n' : fiore@0: if(!(evt.getSource() instanceof JTextField)) fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.new_line"),voice); fiore@0: break; fiore@0: case ' ' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.space"),voice); fiore@0: break; fiore@0: case '@' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.at"),voice); fiore@0: break; fiore@3: case '*' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.asterisk"),voice); fiore@3: break; fiore@3: case '$' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.dollar"),voice); fiore@3: break; fiore@0: case '.' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.dot"),voice); fiore@0: break; fiore@0: case ',' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.comma"),voice); fiore@0: break; fiore@0: case ';' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.semi_colon"),voice); fiore@0: break; fiore@0: case ':' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.colon"),voice); fiore@0: break; fiore@0: case '<' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.lower_than"),voice); fiore@0: break; fiore@0: case '>' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.greater_than"),voice); fiore@0: break; fiore@0: case '#' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.sharp"),voice); fiore@0: break; fiore@0: case '~' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.tilde"),voice); fiore@0: break; fiore@0: case '+' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.plus"),voice); fiore@0: break; fiore@0: case '-' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.dash"),voice); fiore@0: break; fiore@0: case '_' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.underscore"),voice); fiore@0: break; fiore@0: case '/' : fiore@3: NarratorFactory.getInstance().speak(resources.getString("char.slash"),voice); fiore@0: break; fiore@0: } fiore@0: } fiore@0: } fiore@0: isTab = false; fiore@0: } fiore@0: fiore@0: /* manages all the non digit or letter characters */ fiore@0: @Override fiore@0: public void keyPressed(KeyEvent e){ fiore@0: int caretPos = ((JTextComponent)e.getSource()).getCaretPosition(); fiore@0: String text = ((JTextComponent)e.getSource()).getText(); fiore@0: fiore@0: if (e.getKeyCode() == KeyEvent.VK_TAB){ fiore@0: isTab = true; fiore@0: } fiore@0: if(caretPos == 0) fiore@0: isBeginning = true; fiore@0: else fiore@0: isBeginning = false; fiore@0: fiore@0: isFirstLine = true; fiore@0: for(int i=0; i