1
17 package guk.im;
18
19 import java.awt.*;
20 import java.awt.event.InputMethodEvent;
21 import java.awt.event.KeyEvent;
22 import java.awt.im.spi.InputMethod;
23 import java.awt.im.spi.InputMethodContext;
24 import java.io.IOException;
25 import java.lang.Character.Subset;
26 import java.text.AttributedString;
27 import java.util.*;
28
29
33 public class GateIM implements InputMethod {
34
35
40 public GateIM(Map supportedLocales) {
41 this.supportedLocales = supportedLocales;
42 loadedLocales = new HashMap();
43 }
45
51 public void setInputMethodContext(InputMethodContext context) {
52 myContext = context;
53 myContext.enableClientWindowNotification(this, false);
55 }
57
62 public boolean setLocale(Locale locale) {
63 endComposition();
64 try {
65 if(supportedLocales.containsKey(locale)){
66 currentLocale = locale;
67 loadLocale(locale);
68 if(keyboardMap != null) keyboardMap.update(currentHandler,
69 currentState);
70 return true;
71 }
72 } catch(IllegalArgumentException iae){
73 iae.printStackTrace();
74 return false;
75 }
76 return false;
77 }
79
83 public Locale getLocale() {
84 return currentLocale;
85 }
86
87
91 public GateIMDescriptor getDescriptor(){
92 return new GateIMDescriptor();
93 }
94
95
101 public void setCharacterSubsets(Subset[] subsets) {
102 }
103
104
109 public void setCompositionEnabled(boolean enable) {
110 enabled = enable;
111 }
112
113
117 public boolean isCompositionEnabled() {
118 return enabled;
119 }
120
121
126 public void reconvert() {
127
128 throw new java.lang.UnsupportedOperationException(
129 "Reconversion not supported!");
130 }
131
132
141 public void dispatchEvent(AWTEvent event) {
142 if(event instanceof KeyEvent){
143 KeyEvent kEvent = (KeyEvent) event;
144 char ch = kEvent.getKeyChar();
145 int keyCode = kEvent.getKeyCode();
146 int modifiers = kEvent.getModifiers();
147 int id = kEvent.getID();
148 if((id == KeyEvent.KEY_PRESSED || id == KeyEvent.KEY_RELEASED) &&
150 (modifiers & KeyEvent.CTRL_MASK) > 0 &&
151 keyCode != KeyEvent.VK_CONTROL){
152 boolean shift = (modifiers & KeyEvent.SHIFT_MASK) > 0;
153 if(ch == KeyEvent.CHAR_UNDEFINED ||
154 Character.isISOControl(ch)){
155 if((int)'0' <= keyCode && keyCode <= (int)'9'){
156 if(!shift){
157 ch = (char)keyCode;
158 }else{
159 switch((char)keyCode){
161 case '0':{
162 ch = ')';
163 break;
164 }
165 case '1':{
166 ch = '!';
167 break;
168 }
169 case '2':{
170 ch = '\"';
171 break;
172 }
173 case '3':{
174 ch = '\u00a3'; break;
176 }
177 case '4':{
178 ch = '$';
179 break;
180 }
181 case '5':{
182 ch = '%';
183 break;
184 }
185 case '6':{
186 ch = '^';
187 break;
188 }
189 case '7':{
190 ch = '&';
191 break;
192 }
193 case '8':{
194 ch = '*';
195 break;
196 }
197 case '9':{
198 ch = '(';
199 break;
200 }
201 } }
203 } else if((int)'A' <= keyCode && keyCode <= (int)'Z'){
204 ch = (char)keyCode;
205 if(!shift){
206 ch = Character.toLowerCase(ch);
207 }
208 } else {
209 switch(keyCode){
210 case KeyEvent.VK_MINUS:{
211 ch = shift?'_':'-';
212 break;
213 }
214 case KeyEvent.VK_EQUALS:{
215 ch = shift?'+':'=';
216 break;
217 }
218 case KeyEvent.VK_OPEN_BRACKET:{
219 ch = shift?'{':'[';
220 break;
221 }
222 case KeyEvent.VK_CLOSE_BRACKET:{
223 ch = shift?'}':']';
224 break;
225 }
226 case KeyEvent.VK_SEMICOLON:{
227 ch = shift?':':';';
228 break;
229 }
230 case KeyEvent.VK_BACK_QUOTE:{
231 ch = shift?'@':'\'';
232 break;
233 }
234 case KeyEvent.VK_QUOTE:{
235 ch = shift?'~':'#';
236 break;
237 }
238 case KeyEvent.VK_BACK_SLASH:{
239 ch = shift?'|':'\\';
240 break;
241 }
242 case KeyEvent.VK_COMMA:{
243 ch = shift?'<':',';
244 break;
245 }
246 case KeyEvent.VK_STOP:{
247 ch = shift?'>':'.';
248 break;
249 }
250 case KeyEvent.VK_SLASH:{
251 ch = shift?'?':'/';
252 break;
253 }
254 }
255 }
256 } if(id == KeyEvent.KEY_PRESSED) id = KeyEvent.KEY_TYPED;
259 }
260
261 ((KeyEvent)event).setKeyChar(ch);
263 if(keyboardMap != null) keyboardMap.addJob(event);
264
265 if( id == KeyEvent.KEY_TYPED &&
267 ( ch == ' ' ||
268 !Character.isISOControl(ch)
269 )
270 )
271 {
272 Key key = new Key(ch, modifiers);
274 Action action = currentState.getNext(key);
275 if(action == null){
276 if(currentState.isFinal()){
278 myContext.dispatchInputMethodEvent(
279 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
280 (new AttributedString(composedText)).getIterator(),
281 composedText.length(), null, null);
282 }
283 composedText = "";
285 currentState = currentHandler.getInitialState();
286 action = currentState.getNext(key);
287 }
288 if(action ==null){
289 composedText = "";
292 } else {
293 currentState = action.getNext();
295 composedText = action.getComposedText();
296 }
297 ((KeyEvent)event).consume();
298 boolean commit = !currentState.hasNext();
300 myContext.dispatchInputMethodEvent(
301 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
302 (new AttributedString(composedText)).getIterator(),
303 commit?composedText.length():0, null,null);
304 if(commit) composedText = "";
305 if(keyboardMap != null) keyboardMap.update(currentHandler, currentState);
306 }
307 } }
310
316 public void notifyClientWindowChange(Rectangle bounds) {
317 }
320
321
325 public void activate() {
326 enabled = true;
327 if(currentLocale == null) setLocale(defaultLocale);
328 if(mapVisible){
329 if(keyboardMap == null) keyboardMap = new KeyboardMap(this,
330 currentHandler,
331 currentState);
332 keyboardMap.addJob("SHOW");
333 }
334 }
336
341 public void deactivate(boolean isTemporary) {
342 endComposition();
343 enabled = false;
344 }
347
352 public void hideWindows() {
353 if(mapVisible) keyboardMap.addJob("HIDE");
354 }
355
356
361 public void removeNotify() {
362 }
364
365
369 public void endComposition() {
370 if(composedText.length() > 0 && currentState.isFinal()){
372 myContext.dispatchInputMethodEvent(
373 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
374 (new AttributedString(composedText)).getIterator(),
375 composedText.length(), null, null);
376 }
377 composedText = "";
378 }
379
380
384 public void dispose() {
385 endComposition();
386 if(keyboardMap != null){
387 keyboardMap.addJob("DIE");
388 keyboardMap = null;
389 }
390 currentLocale = null;
391 currentHandler = null;
392 currentState = null;
393 myContext = null;
394 supportedLocales.clear();
395 supportedLocales = null;
396 loadedLocales.clear();
397 loadedLocales = null;
398 }
399
400
406 public Object getControlObject() {
407 return this;
408 }
409
410
415 public void setMapVisible(boolean mapvis) {
416 if(mapvis){
417 mapVisible = true;
418 if(keyboardMap == null) keyboardMap = new KeyboardMap(this,
419 currentHandler,
420 currentState);
421 keyboardMap.addJob("SHOW");
422 }else{
423 mapVisible = false;
424 if(keyboardMap != null) keyboardMap.addJob("HIDE");
425 }
426 }
428
433 protected void loadLocale(Locale locale){
434 String fileName = (String)supportedLocales.get(locale);
435 if(fileName == null) throw new IllegalArgumentException(
436 "Unknown locale: " + locale);
437 currentHandler = (LocaleHandler)loadedLocales.get(locale);
438 if(currentHandler == null){
439 try {
440 currentHandler = new LocaleHandler(locale, fileName);
441 loadedLocales.put(locale, currentHandler);
442 currentState = currentHandler.getInitialState();
443 } catch(IOException ioe) {
444 throw new IllegalArgumentException("Cannot load locale: " + locale);
445 }
446 } }
449
453 public InputMethodContext getContext(){
454 return myContext;
455 }
456
457
462 Locale currentLocale;
463
468 Locale defaultLocale = new Locale("en", "", "ASCII");
469
473 LocaleHandler currentHandler;
474
478 InputMethodContext myContext;
479
484 Map supportedLocales;
485
491 Map loadedLocales;
492
496 boolean enabled;
497
501 String composedText = "";
502
506 State currentState;
507
511 Map additionalKeymaps;
512
516 static KeyboardMap keyboardMap;
517
521 boolean mapVisible = true;
522
523
525 static private String imBase = "/guk/im/data/";
526
527
533 static public void setIMBase(String path){
534 imBase = path;
535 }
536
537
539 public static String getIMBase(){return imBase;}
540
541
542 static private Font keyboardFont = new Font("Arial Unicode MS", Font.PLAIN, 12);
543 public static Font getKeyboardFont(){
544 return keyboardFont;
545 }
546 }