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.gui;
|
f@0
|
21
|
f@0
|
22 import java.awt.event.ActionEvent;
|
f@0
|
23 import java.awt.event.ItemEvent;
|
f@0
|
24 import java.awt.event.ItemListener;
|
f@0
|
25 import java.awt.event.KeyEvent;
|
f@0
|
26 import java.awt.event.MouseEvent;
|
f@0
|
27 import java.text.MessageFormat;
|
f@0
|
28 import java.util.ResourceBundle;
|
f@0
|
29
|
f@0
|
30 import javax.swing.AbstractAction;
|
f@0
|
31 import javax.swing.Action;
|
f@0
|
32 import javax.swing.ComponentInputMap;
|
f@0
|
33 import javax.swing.JCheckBoxMenuItem;
|
f@0
|
34 import javax.swing.JComponent;
|
f@0
|
35 import javax.swing.JMenu;
|
f@0
|
36 import javax.swing.JMenuBar;
|
f@0
|
37 import javax.swing.JMenuItem;
|
f@0
|
38 import javax.swing.KeyStroke;
|
f@0
|
39 import javax.swing.MenuElement;
|
f@0
|
40 import javax.swing.MenuSelectionManager;
|
f@0
|
41
|
f@0
|
42 import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
|
f@0
|
43 import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
|
f@0
|
44 import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
|
f@0
|
45
|
f@0
|
46 /*
|
f@0
|
47 * This class provides a version of JMenuItem, JCheckBox, and JMenu which emit an "error" sound when
|
f@0
|
48 * they're disabled and the user tries to use it by an accelerator
|
f@0
|
49 */
|
f@0
|
50 @SuppressWarnings("serial")
|
f@0
|
51 class SpeechMenuFactory extends JMenu {
|
f@0
|
52
|
f@0
|
53 /* implements the singleton pattern and keeps a static reference to the menuBar used by JMenuItems and JMenus */
|
f@0
|
54 public static JMenuBar getMenuBar(){
|
f@0
|
55 if(menuBar == null){
|
f@0
|
56 menuBar = new SpeechMenuBar();
|
f@0
|
57 }
|
f@0
|
58 return menuBar;
|
f@0
|
59 }
|
f@0
|
60
|
f@0
|
61 public static JMenuItem getMenuItem(String text){
|
f@0
|
62 return new SpeechMenuItem(text);
|
f@0
|
63 }
|
f@0
|
64
|
f@0
|
65 public static JMenu getMenu(String text){
|
f@0
|
66 return new JMenu(text){
|
f@0
|
67 @Override
|
f@0
|
68 public void menuSelectionChanged(boolean isIncluded){
|
f@0
|
69 super.menuSelectionChanged(isIncluded);
|
f@0
|
70 if(isIncluded && !wasMouse){
|
f@0
|
71 String menuType = resources.getString("menufactory.menu");
|
f@0
|
72 if(getMenuBar().getComponentIndex(this) == -1){
|
f@0
|
73 menuType = resources.getString("menufactory.submenu");
|
f@0
|
74 };
|
f@0
|
75 NarratorFactory.getInstance().speak(getAccessibleContext().getAccessibleName()+ " "+ menuType);
|
f@0
|
76 }
|
f@0
|
77 wasMouse = false;
|
f@0
|
78 }
|
f@0
|
79
|
f@0
|
80 @Override
|
f@0
|
81 public void processMouseEvent(MouseEvent e){
|
f@0
|
82 wasMouse = true;
|
f@0
|
83 super.processMouseEvent(e);
|
f@0
|
84 }
|
f@0
|
85
|
f@0
|
86 private boolean wasMouse = false;
|
f@0
|
87 };
|
f@0
|
88 }
|
f@0
|
89
|
f@0
|
90 public static JCheckBoxMenuItem getJCheckBoxMenuItem(String text){
|
f@0
|
91 return new SpeechJCheckBoxMenuItem(text);
|
f@0
|
92 }
|
f@0
|
93
|
f@0
|
94 /* this action is called when the user strokes a disabled menu accelerator */
|
f@0
|
95 private static Action errorAction = new AbstractAction(){
|
f@0
|
96 @Override
|
f@0
|
97 public void actionPerformed(ActionEvent e) {
|
f@0
|
98 SoundFactory.getInstance().play(SoundEvent.ERROR);
|
f@0
|
99 }
|
f@0
|
100 };
|
f@0
|
101
|
f@0
|
102 private static class SpeechMenuBar extends JMenuBar{
|
f@0
|
103 SpeechMenuBar() {
|
f@0
|
104 setInputMap(SpeechMenuBar.WHEN_IN_FOCUSED_WINDOW, new ComponentInputMap(this){
|
f@0
|
105 @Override
|
f@0
|
106 public Object get(KeyStroke keyStroke){
|
f@0
|
107 if(keyStroke == null)
|
f@0
|
108 return null;
|
f@0
|
109 return super.get(keyStroke);
|
f@0
|
110 }
|
f@0
|
111 });
|
f@0
|
112 }
|
f@0
|
113
|
f@0
|
114 @Override
|
f@0
|
115 public void processKeyEvent(KeyEvent e, MenuElement[] path, MenuSelectionManager manager) {
|
f@0
|
116 super.processKeyEvent(e,path,manager);
|
f@0
|
117 if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
|
f@0
|
118 NarratorFactory.getInstance().speak(resources.getString("menufactory.leaving"));
|
f@0
|
119 }
|
f@0
|
120 }
|
f@0
|
121 }
|
f@0
|
122
|
f@0
|
123 /*
|
f@0
|
124 * this class implements a menu item which speaks out its label when
|
f@0
|
125 * selected with the keyboard (mouse hover will have no effect)
|
f@0
|
126 * it needs a reference to the menu bar it's in to implement a respond, with the error sound, to
|
f@0
|
127 * the accelerator even when it's disabled (normally no listeners are called otherwise)
|
f@0
|
128 *
|
f@0
|
129 */
|
f@0
|
130 private static class SpeechMenuItem extends JMenuItem{
|
f@0
|
131 @Override
|
f@0
|
132 public void processMouseEvent(MouseEvent e ){
|
f@0
|
133 wasMouse = true;
|
f@0
|
134 super.processMouseEvent(e);
|
f@0
|
135 }
|
f@0
|
136
|
f@0
|
137 public SpeechMenuItem(String text){
|
f@0
|
138 super(text);
|
f@0
|
139 /* bind ACCELERATOR with the error action */
|
f@0
|
140 getMenuBar().getActionMap().put(ACCELERATOR, errorAction);
|
f@0
|
141 wasMouse = false;
|
f@0
|
142 if(text.trim().endsWith("...")){
|
f@0
|
143 /* replace the ... in the accessible name with the voice version of it */
|
f@0
|
144 String accName = getAccessibleContext().getAccessibleName().replaceAll("...\\s*$", "");
|
f@0
|
145 getAccessibleContext().setAccessibleName(accName +" "+ resources.getString("menufactory.3dot"));
|
f@0
|
146 }
|
f@0
|
147 }
|
f@0
|
148
|
f@0
|
149 @Override
|
f@0
|
150 public void menuSelectionChanged(boolean isIncluded){
|
f@0
|
151 super.menuSelectionChanged(isIncluded);
|
f@0
|
152 if(isIncluded && !wasMouse){
|
f@0
|
153 String disabled = isEnabled() ? "" : resources.getString("menufactory.disabled");
|
f@0
|
154 String accelerator = "";
|
f@0
|
155 if(getAccelerator() != null){
|
f@0
|
156 accelerator = resources.getString("menufactory.ctrl")+" "+ getAccelerator().toString().substring(getAccelerator().toString().lastIndexOf(' '));
|
f@0
|
157 }
|
f@0
|
158 NarratorFactory.getInstance().speak(getAccessibleContext().getAccessibleName()+disabled+accelerator);
|
f@0
|
159 }
|
f@0
|
160 wasMouse = false;
|
f@0
|
161 }
|
f@0
|
162
|
f@0
|
163 @Override
|
f@0
|
164 public void setEnabled(boolean b){
|
f@0
|
165 super.setEnabled(b);
|
f@0
|
166 if(getAccelerator() == null)
|
f@0
|
167 return;
|
f@0
|
168 if(b == false){
|
f@0
|
169 /* if the menu item gets disabled, then set up an action in the menuBar to respond to
|
f@0
|
170 * the accelerator key stroke, so that the user gets a feedback (error sound)
|
f@0
|
171 * even though the listeners don't get called (as the menu item is disabled)
|
f@0
|
172 */
|
f@0
|
173 getMenuBar().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(getAccelerator(), ACCELERATOR);
|
f@0
|
174 }else{
|
f@0
|
175 getMenuBar().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(getAccelerator(), "none");
|
f@0
|
176 }
|
f@0
|
177 }
|
f@0
|
178
|
f@0
|
179 private boolean wasMouse;
|
f@0
|
180 };
|
f@0
|
181
|
f@0
|
182 private static class SpeechJCheckBoxMenuItem extends JCheckBoxMenuItem {
|
f@0
|
183 public SpeechJCheckBoxMenuItem(String text){
|
f@0
|
184 super(text);
|
f@0
|
185 addItemListener(new ItemListener(){
|
f@0
|
186 @Override
|
f@0
|
187 public void itemStateChanged(ItemEvent evt) {
|
f@0
|
188 int stateChange = evt.getStateChange();
|
f@0
|
189 if(stateChange != ItemEvent.SELECTED && stateChange != ItemEvent.DESELECTED){
|
f@0
|
190 return;
|
f@0
|
191 }
|
f@0
|
192 if(!itemChangeMouseFlag){
|
f@0
|
193 NarratorFactory.getInstance().speak(
|
f@0
|
194 MessageFormat.format(
|
f@0
|
195 resources.getString(stateChange == ItemEvent.SELECTED ? "menufactory.selected" : "menufactory.unselected"),
|
f@0
|
196 getAccessibleContext().getAccessibleName()));
|
f@0
|
197 }
|
f@0
|
198 itemChangeMouseFlag = false;
|
f@0
|
199 }
|
f@0
|
200 });
|
f@0
|
201 }
|
f@0
|
202
|
f@0
|
203 @Override
|
f@0
|
204 public void menuSelectionChanged(boolean isIncluded){
|
f@0
|
205 super.menuSelectionChanged(isIncluded);
|
f@0
|
206 if(isIncluded && !selectionMouseFlag ){
|
f@0
|
207 NarratorFactory.getInstance().speak(
|
f@0
|
208 MessageFormat.format(
|
f@0
|
209 resources.getString(isSelected() ? "menufactory.selected" : "menufactory.unselected"),
|
f@0
|
210 getAccessibleContext().getAccessibleName()));
|
f@0
|
211 }
|
f@0
|
212 selectionMouseFlag = false;
|
f@0
|
213 }
|
f@0
|
214
|
f@0
|
215 @Override
|
f@0
|
216 public void processMouseEvent(MouseEvent e){
|
f@0
|
217 selectionMouseFlag = true;
|
f@0
|
218 itemChangeMouseFlag = true;
|
f@0
|
219 super.processMouseEvent(e);
|
f@0
|
220 }
|
f@0
|
221
|
f@0
|
222 private boolean selectionMouseFlag = false;
|
f@0
|
223 private boolean itemChangeMouseFlag = false;
|
f@0
|
224 }
|
f@0
|
225
|
f@0
|
226 private static JMenuBar menuBar;
|
f@0
|
227 private static ResourceBundle resources = ResourceBundle.getBundle(EditorFrame.class.getName());
|
f@0
|
228 private static final String ACCELERATOR = "accelerator";
|
f@0
|
229 }
|