f@0: /* f@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool f@0: f@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/) f@0: f@0: This program is free software: you can redistribute it and/or modify f@0: it under the terms of the GNU General Public License as published by f@0: the Free Software Foundation, either version 3 of the License, or f@0: (at your option) any later version. f@0: f@0: This program is distributed in the hope that it will be useful, f@0: but WITHOUT ANY WARRANTY; without even the implied warranty of f@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the f@0: GNU General Public License for more details. f@0: f@0: You should have received a copy of the GNU General Public License f@0: along with this program. If not, see . f@0: */ f@0: f@0: package uk.ac.qmul.eecs.ccmi.gui; f@0: f@0: import java.awt.event.ActionEvent; f@0: import java.awt.event.ItemEvent; f@0: import java.awt.event.ItemListener; f@0: import java.awt.event.KeyEvent; f@0: import java.awt.event.MouseEvent; f@0: import java.text.MessageFormat; f@0: import java.util.ResourceBundle; f@0: f@0: import javax.swing.AbstractAction; f@0: import javax.swing.Action; f@0: import javax.swing.ComponentInputMap; f@0: import javax.swing.JCheckBoxMenuItem; f@0: import javax.swing.JComponent; f@0: import javax.swing.JMenu; f@0: import javax.swing.JMenuBar; f@0: import javax.swing.JMenuItem; f@0: import javax.swing.KeyStroke; f@0: import javax.swing.MenuElement; f@0: import javax.swing.MenuSelectionManager; f@0: f@0: import uk.ac.qmul.eecs.ccmi.sound.SoundEvent; f@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory; f@0: import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory; f@0: f@0: /* f@0: * This class provides a version of JMenuItem, JCheckBox, and JMenu which emit an "error" sound when f@0: * they're disabled and the user tries to use it by an accelerator f@0: */ f@0: @SuppressWarnings("serial") f@0: class SpeechMenuFactory extends JMenu { f@0: f@0: /* implements the singleton pattern and keeps a static reference to the menuBar used by JMenuItems and JMenus */ f@0: public static JMenuBar getMenuBar(){ f@0: if(menuBar == null){ f@0: menuBar = new SpeechMenuBar(); f@0: } f@0: return menuBar; f@0: } f@0: f@0: public static JMenuItem getMenuItem(String text){ f@0: return new SpeechMenuItem(text); f@0: } f@0: f@0: public static JMenu getMenu(String text){ f@0: return new JMenu(text){ f@0: @Override f@0: public void menuSelectionChanged(boolean isIncluded){ f@0: super.menuSelectionChanged(isIncluded); f@0: if(isIncluded && !wasMouse){ f@0: String menuType = resources.getString("menufactory.menu"); f@0: if(getMenuBar().getComponentIndex(this) == -1){ f@0: menuType = resources.getString("menufactory.submenu"); f@0: }; f@0: NarratorFactory.getInstance().speak(getAccessibleContext().getAccessibleName()+ " "+ menuType); f@0: } f@0: wasMouse = false; f@0: } f@0: f@0: @Override f@0: public void processMouseEvent(MouseEvent e){ f@0: wasMouse = true; f@0: super.processMouseEvent(e); f@0: } f@0: f@0: private boolean wasMouse = false; f@0: }; f@0: } f@0: f@0: public static JCheckBoxMenuItem getJCheckBoxMenuItem(String text){ f@0: return new SpeechJCheckBoxMenuItem(text); f@0: } f@0: f@0: /* this action is called when the user strokes a disabled menu accelerator */ f@0: private static Action errorAction = new AbstractAction(){ f@0: @Override f@0: public void actionPerformed(ActionEvent e) { f@0: SoundFactory.getInstance().play(SoundEvent.ERROR); f@0: } f@0: }; f@0: f@0: private static class SpeechMenuBar extends JMenuBar{ f@0: SpeechMenuBar() { f@0: setInputMap(SpeechMenuBar.WHEN_IN_FOCUSED_WINDOW, new ComponentInputMap(this){ f@0: @Override f@0: public Object get(KeyStroke keyStroke){ f@0: if(keyStroke == null) f@0: return null; f@0: return super.get(keyStroke); f@0: } f@0: }); f@0: } f@0: f@0: @Override f@0: public void processKeyEvent(KeyEvent e, MenuElement[] path, MenuSelectionManager manager) { f@0: super.processKeyEvent(e,path,manager); f@0: if(e.getKeyCode() == KeyEvent.VK_ESCAPE){ f@0: NarratorFactory.getInstance().speak(resources.getString("menufactory.leaving")); f@0: } f@0: } f@0: } f@0: f@0: /* f@0: * this class implements a menu item which speaks out its label when f@0: * selected with the keyboard (mouse hover will have no effect) f@0: * it needs a reference to the menu bar it's in to implement a respond, with the error sound, to f@0: * the accelerator even when it's disabled (normally no listeners are called otherwise) f@0: * f@0: */ f@0: private static class SpeechMenuItem extends JMenuItem{ f@0: @Override f@0: public void processMouseEvent(MouseEvent e ){ f@0: wasMouse = true; f@0: super.processMouseEvent(e); f@0: } f@0: f@0: public SpeechMenuItem(String text){ f@0: super(text); f@0: /* bind ACCELERATOR with the error action */ f@0: getMenuBar().getActionMap().put(ACCELERATOR, errorAction); f@0: wasMouse = false; f@0: if(text.trim().endsWith("...")){ f@0: /* replace the ... in the accessible name with the voice version of it */ f@0: String accName = getAccessibleContext().getAccessibleName().replaceAll("...\\s*$", ""); f@0: getAccessibleContext().setAccessibleName(accName +" "+ resources.getString("menufactory.3dot")); f@0: } f@0: } f@0: f@0: @Override f@0: public void menuSelectionChanged(boolean isIncluded){ f@0: super.menuSelectionChanged(isIncluded); f@0: if(isIncluded && !wasMouse){ f@0: String disabled = isEnabled() ? "" : resources.getString("menufactory.disabled"); f@0: String accelerator = ""; f@0: if(getAccelerator() != null){ f@0: accelerator = resources.getString("menufactory.ctrl")+" "+ getAccelerator().toString().substring(getAccelerator().toString().lastIndexOf(' ')); f@0: } f@0: NarratorFactory.getInstance().speak(getAccessibleContext().getAccessibleName()+disabled+accelerator); f@0: } f@0: wasMouse = false; f@0: } f@0: f@0: @Override f@0: public void setEnabled(boolean b){ f@0: super.setEnabled(b); f@0: if(getAccelerator() == null) f@0: return; f@0: if(b == false){ f@0: /* if the menu item gets disabled, then set up an action in the menuBar to respond to f@0: * the accelerator key stroke, so that the user gets a feedback (error sound) f@0: * even though the listeners don't get called (as the menu item is disabled) f@0: */ f@0: getMenuBar().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(getAccelerator(), ACCELERATOR); f@0: }else{ f@0: getMenuBar().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(getAccelerator(), "none"); f@0: } f@0: } f@0: f@0: private boolean wasMouse; f@0: }; f@0: f@0: private static class SpeechJCheckBoxMenuItem extends JCheckBoxMenuItem { f@0: public SpeechJCheckBoxMenuItem(String text){ f@0: super(text); f@0: addItemListener(new ItemListener(){ f@0: @Override f@0: public void itemStateChanged(ItemEvent evt) { f@0: int stateChange = evt.getStateChange(); f@0: if(stateChange != ItemEvent.SELECTED && stateChange != ItemEvent.DESELECTED){ f@0: return; f@0: } f@0: if(!itemChangeMouseFlag){ f@0: NarratorFactory.getInstance().speak( f@0: MessageFormat.format( f@0: resources.getString(stateChange == ItemEvent.SELECTED ? "menufactory.selected" : "menufactory.unselected"), f@0: getAccessibleContext().getAccessibleName())); f@0: } f@0: itemChangeMouseFlag = false; f@0: } f@0: }); f@0: } f@0: f@0: @Override f@0: public void menuSelectionChanged(boolean isIncluded){ f@0: super.menuSelectionChanged(isIncluded); f@0: if(isIncluded && !selectionMouseFlag ){ f@0: NarratorFactory.getInstance().speak( f@0: MessageFormat.format( f@0: resources.getString(isSelected() ? "menufactory.selected" : "menufactory.unselected"), f@0: getAccessibleContext().getAccessibleName())); f@0: } f@0: selectionMouseFlag = false; f@0: } f@0: f@0: @Override f@0: public void processMouseEvent(MouseEvent e){ f@0: selectionMouseFlag = true; f@0: itemChangeMouseFlag = true; f@0: super.processMouseEvent(e); f@0: } f@0: f@0: private boolean selectionMouseFlag = false; f@0: private boolean itemChangeMouseFlag = false; f@0: } f@0: f@0: private static JMenuBar menuBar; f@0: private static ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName()); f@0: private static final String ACCELERATOR = "accelerator"; f@0: }