|
GateIM |
|
1 /* 2 * GateIM.java 3 * 4 * Copyright (c) 2000-2001, The University of Sheffield. 5 * 6 * This file is part of GATE (see http://gate.ac.uk/), and is free 7 * software, licenced under the GNU Library General Public License, 8 * Version 2, June1991. 9 * 10 * A copy of this licence is included in the distribution in the file 11 * licence.html, and is also available at http://gate.ac.uk/gate/licence.html. 12 * 13 * Valentin Tablan, October 2000 14 * 15 * $Id: GateIM.java,v 1.4 2001/11/18 15:31:44 valyt Exp $ 16 */ 17 package guk.im; 18 19 import java.awt.im.spi.InputMethodContext; 20 import java.util.Locale; 21 import java.awt.AWTEvent; 22 import java.awt.Window; 23 import java.awt.Frame; 24 import java.awt.Component; 25 import java.awt.Font; 26 import java.awt.event.*; 27 import java.awt.event.InputEvent; 28 import java.awt.Rectangle; 29 import java.awt.im.spi.InputMethod; 30 import java.lang.Character.Subset; 31 import java.util.*; 32 import java.io.*; 33 import java.text.*; 34 35 import guk.*; 36 37 /** 38 * The Gate input method 39 * 40 */ 41 public class GateIM implements InputMethod { 42 43 /** 44 * Constructs a new Gate input method 45 * 46 * @param supportedLocales 47 */ 48 public GateIM(Map supportedLocales) { 49 this.supportedLocales = supportedLocales; 50 loadedLocales = new HashMap(); 51 }// GateIM(Map supportedLocales) 52 53 /** 54 * Provides the input method with a context. This method is called by the 55 * system after the input method is loaded and linked to a text component. 56 * 57 * @param context 58 */ 59 public void setInputMethodContext(InputMethodContext context) { 60 myContext = context; 61 //we don't care about the client window state and position 62 myContext.enableClientWindowNotification(this, false); 63 }// setInputMethodContext(InputMethodContext context) 64 65 /** 66 * Selects the active locale 67 * 68 * @param locale 69 */ 70 public boolean setLocale(Locale locale) { 71 endComposition(); 72 try { 73 if(supportedLocales.containsKey(locale)){ 74 currentLocale = locale; 75 loadLocale(locale); 76 if(keyboardMap != null) keyboardMap.update(currentHandler, 77 currentState); 78 return true; 79 } 80 } catch(IllegalArgumentException iae){ 81 iae.printStackTrace(); 82 return false; 83 } 84 return false; 85 }// boolean setLocale(Locale locale) 86 87 /** 88 * Gets the active locale 89 * 90 */ 91 public Locale getLocale() { 92 return currentLocale; 93 } 94 95 /** 96 * gets the descriptor class for this input method 97 * 98 */ 99 public GateIMDescriptor getDescriptor(){ 100 return new GateIMDescriptor(); 101 } 102 103 /** 104 * Restricts the character ranges valid for this input method output. This is 105 * currently ignored by the input method. 106 * 107 * @param subsets 108 */ 109 public void setCharacterSubsets(Subset[] subsets) { 110 } 111 112 /** 113 * Enables this input method for composition 114 * 115 * @param enable 116 */ 117 public void setCompositionEnabled(boolean enable) { 118 enabled = enable; 119 } 120 121 /** 122 * Is this input method enabled? 123 * 124 */ 125 public boolean isCompositionEnabled() { 126 return enabled; 127 } 128 129 /** 130 * Throws a UnsupportedOperationException as this input method does not 131 * support recnversion. 132 * 133 */ 134 public void reconvert() { 135 /**@todo: Implement this java.awt.im.spi.InputMethod method*/ 136 throw new java.lang.UnsupportedOperationException( 137 "Reconversion not supported!"); 138 } 139 140 /** 141 * Called by the system when an input event occures in a component that uses 142 * this input method. 143 * The input method then analyses the input event and sends an input method 144 * event to the interested components 145 * using the input context provided by the system. 146 * 147 * @param event 148 */ 149 public void dispatchEvent(AWTEvent event) { 150 if(event instanceof KeyEvent){ 151 KeyEvent kEvent = (KeyEvent) event; 152 char ch = kEvent.getKeyChar(); 153 int keyCode = kEvent.getKeyCode(); 154 int modifiers = kEvent.getModifiers(); 155 int id = kEvent.getID(); 156 //process the CTRL+? events that do not generate key-typed events. 157 if((id == KeyEvent.KEY_PRESSED || id == KeyEvent.KEY_RELEASED) && 158 (modifiers & KeyEvent.CTRL_MASK) > 0 && 159 keyCode != KeyEvent.VK_CONTROL){ 160 boolean shift = (modifiers & KeyEvent.SHIFT_MASK) > 0; 161 if(ch == KeyEvent.CHAR_UNDEFINED || 162 Character.isISOControl(ch)){ 163 if((int)'0' <= keyCode && keyCode <= (int)'9'){ 164 if(!shift){ 165 ch = (char)keyCode; 166 }else{ 167 //shifted versions for the digit keys 168 switch((char)keyCode){ 169 case '0':{ 170 ch = ')'; 171 break; 172 } 173 case '1':{ 174 ch = '!'; 175 break; 176 } 177 case '2':{ 178 ch = '\"'; 179 break; 180 } 181 case '3':{ 182 ch = '£'; 183 break; 184 } 185 case '4':{ 186 ch = '$'; 187 break; 188 } 189 case '5':{ 190 ch = '%'; 191 break; 192 } 193 case '6':{ 194 ch = '^'; 195 break; 196 } 197 case '7':{ 198 ch = '&'; 199 break; 200 } 201 case '8':{ 202 ch = '*'; 203 break; 204 } 205 case '9':{ 206 ch = '('; 207 break; 208 } 209 }//switch((char)keyCode) 210 } 211 } else if((int)'A' <= keyCode && keyCode <= (int)'Z'){ 212 ch = (char)keyCode; 213 if(!shift){ 214 ch = Character.toLowerCase(ch); 215 } 216 } else { 217 switch(keyCode){ 218 case KeyEvent.VK_MINUS:{ 219 ch = shift?'_':'-'; 220 break; 221 } 222 case KeyEvent.VK_EQUALS:{ 223 ch = shift?'+':'='; 224 break; 225 } 226 case KeyEvent.VK_OPEN_BRACKET:{ 227 ch = shift?'{':'['; 228 break; 229 } 230 case KeyEvent.VK_CLOSE_BRACKET:{ 231 ch = shift?'}':']'; 232 break; 233 } 234 case KeyEvent.VK_SEMICOLON:{ 235 ch = shift?':':';'; 236 break; 237 } 238 case KeyEvent.VK_BACK_QUOTE:{ 239 ch = shift?'@':'\''; 240 break; 241 } 242 case KeyEvent.VK_QUOTE:{ 243 ch = shift?'~':'#'; 244 break; 245 } 246 case KeyEvent.VK_BACK_SLASH:{ 247 ch = shift?'|':'\\'; 248 break; 249 } 250 case KeyEvent.VK_COMMA:{ 251 ch = shift?'<':','; 252 break; 253 } 254 case KeyEvent.VK_STOP:{ 255 ch = shift?'>':'.'; 256 break; 257 } 258 case KeyEvent.VK_SLASH:{ 259 ch = shift?'?':'/'; 260 break; 261 } 262 } 263 } 264 }//if(ch = KeyEvent.CHAR_UNDEFINED) 265 //modify the event 266 if(id == KeyEvent.KEY_PRESSED) id = KeyEvent.KEY_TYPED; 267 } 268 269 //now send it to the virtual keyboard 270 ((KeyEvent)event).setKeyChar(ch); 271 if(keyboardMap != null) keyboardMap.addJob(event); 272 273 //now process it for input 274 if( id == KeyEvent.KEY_TYPED && 275 ( ch == ' ' || 276 !Character.isISOControl(ch) 277 ) 278 ) 279 { 280 //it's a key typed event 281 Key key = new Key(ch, modifiers); 282 Action action = currentState.getNext(key); 283 if(action == null){ 284 //we can't go further, commit if in final state cancel otherwise 285 if(currentState.isFinal()){ 286 myContext.dispatchInputMethodEvent( 287 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, 288 (new AttributedString(composedText)).getIterator(), 289 composedText.length(), null, null); 290 } 291 //move to the initial state 292 composedText = ""; 293 currentState = currentHandler.getInitialState(); 294 action = currentState.getNext(key); 295 } 296 if(action ==null){ 297 //undefined state, remain in initial state, cancel composed text 298 //send the key char 299 composedText = ""; 300 } else { 301 //advance and compose new text 302 currentState = action.getNext(); 303 composedText = action.getComposedText(); 304 } 305 ((KeyEvent)event).consume(); 306 //fire the event to the client 307 boolean commit = !currentState.hasNext(); 308 myContext.dispatchInputMethodEvent( 309 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, 310 (new AttributedString(composedText)).getIterator(), 311 commit?composedText.length():0, null,null); 312 if(commit) composedText = ""; 313 if(keyboardMap != null) keyboardMap.update(currentHandler, currentState); 314 } 315 }// if 316 }// dispatchEvent(AWTEvent event) 317 318 /** 319 * Called by the system when the client window has changed size or position. 320 * This event is ignored by the input method. 321 * 322 * @param bounds 323 */ 324 public void notifyClientWindowChange(Rectangle bounds) { 325 //we don't care about that as we don't display any composition windows 326 //do nothing 327 } 328 329 /** 330 * Activates this input method 331 * 332 */ 333 public void activate() { 334 enabled = true; 335 if(currentLocale == null) setLocale(defaultLocale); 336 if(mapVisible){ 337 if(keyboardMap == null) keyboardMap = new KeyboardMap(this, 338 currentHandler, 339 currentState); 340 keyboardMap.addJob("SHOW"); 341 } 342 }// activate() 343 344 /** 345 * Deactivates this input method 346 * 347 * @param isTemporary 348 */ 349 public void deactivate(boolean isTemporary) { 350 endComposition(); 351 enabled = false; 352 // if(mapVisible) keyboardMap.addJob("HIDE"); 353 }// deactivate(boolean isTemporary) 354 355 /** 356 * Hides all the windows displayed by the input method. Currently this only 357 * includes the virtual keyboard map. 358 * 359 */ 360 public void hideWindows() { 361 if(mapVisible) keyboardMap.addJob("HIDE"); 362 } 363 364 /** 365 * Called by the system when a client unregisters to this input method. This 366 * event is currently ignored by the input method. 367 * 368 */ 369 public void removeNotify() { 370 //so what! :) 371 } 372 373 /** 374 * Ends the curent composition. 375 * 376 */ 377 public void endComposition() { 378 //System.out.println("GateIM endComposition()!"); 379 if(composedText.length() > 0 && currentState.isFinal()){ 380 myContext.dispatchInputMethodEvent( 381 InputMethodEvent.INPUT_METHOD_TEXT_CHANGED, 382 (new AttributedString(composedText)).getIterator(), 383 composedText.length(), null, null); 384 } 385 composedText = ""; 386 } 387 388 /** 389 * Disposes this input method releasing all the memory. 390 * 391 */ 392 public void dispose() { 393 endComposition(); 394 if(keyboardMap != null){ 395 keyboardMap.addJob("DIE"); 396 keyboardMap = null; 397 } 398 currentLocale = null; 399 currentHandler = null; 400 currentState = null; 401 myContext = null; 402 supportedLocales.clear(); 403 supportedLocales = null; 404 loadedLocales.clear(); 405 loadedLocales = null; 406 } 407 408 /** 409 * Gives the clients a chance to control the bevaviour of this input method 410 * by returning a handle to itself. 411 * 412 * @return a reference to this input method 413 */ 414 public Object getControlObject() { 415 return this; 416 } 417 418 /** 419 * Should the virtual keyboard map be visible? 420 * 421 * @param mapvis 422 */ 423 public void setMapVisible(boolean mapvis) { 424 if(mapvis){ 425 mapVisible = true; 426 if(keyboardMap == null) keyboardMap = new KeyboardMap(this, 427 currentHandler, 428 currentState); 429 keyboardMap.addJob("SHOW"); 430 }else{ 431 mapVisible = false; 432 if(keyboardMap != null) keyboardMap.addJob("HIDE"); 433 } 434 }// setMapVisible(boolean mapvis) 435 436 /** 437 * Loads a new locale if it's not already loaded. 438 * 439 * @param locale 440 */ 441 protected void loadLocale(Locale locale){ 442 String fileName = (String)supportedLocales.get(locale); 443 if(fileName == null) throw new IllegalArgumentException( 444 "Unknown locale: " + locale); 445 currentHandler = (LocaleHandler)loadedLocales.get(locale); 446 if(currentHandler == null){ 447 try { 448 currentHandler = new LocaleHandler(locale, fileName); 449 loadedLocales.put(locale, currentHandler); 450 currentState = currentHandler.getInitialState(); 451 } catch(IOException ioe) { 452 throw new IllegalArgumentException("Cannot load locale: " + locale); 453 } 454 }// if 455 }// loadLocale(Locale locale) 456 457 /** 458 * Returns theinput context for this input method. 459 * 460 */ 461 public InputMethodContext getContext(){ 462 return myContext; 463 } 464 465 //--------- variables 466 /** 467 * The active locale 468 * 469 */ 470 Locale currentLocale; 471 /** 472 * The default locale to be used when this method is loaded and no locale is 473 * specified. 474 * 475 */ 476 Locale defaultLocale = new Locale("en", "", "ASCII"); 477 /** 478 * The current locale handler. 479 * 480 */ 481 LocaleHandler currentHandler; 482 /** 483 * The input context 484 * 485 */ 486 InputMethodContext myContext; 487 //maps from Loacle to String (the file name) 488 /** 489 * The available locales (the locales for which a definition file exists). 490 * 491 */ 492 Map supportedLocales; 493 //maps from Locale to LocaleHandler 494 /** 495 * The locales that have been loaded already. Maps from Loacle to 496 * {@link LocaleHandler}. 497 * 498 */ 499 Map loadedLocales; 500 /** 501 * Is this inpuit method enabled? 502 * 503 */ 504 boolean enabled; 505 /** 506 * The composed text; 507 * 508 */ 509 String composedText = ""; 510 /** 511 * The current state of the current LocaleHandler. 512 * 513 */ 514 State currentState; 515 /** 516 * Not used 517 * 518 */ 519 Map additionalKeymaps; 520 /** 521 * The current virtual keyboard map. 522 * 523 */ 524 static KeyboardMap keyboardMap; 525 /** 526 * Should the keyboard map be visible? 527 * 528 */ 529 boolean mapVisible = true; 530 531 /** The resource path to the input methods director 532 */ 533 static private String imBase = "/guk/im/data/"; 534 535 /** Sets the default path to be used when looking for input methods. 536 * This should be a resource path (a path inside the class path). 537 * By default the path is "guk/im/data/" 538 * 539 * @param path 540 */ 541 static public void setIMBase(String path){ 542 imBase = path; 543 } 544 545 /** Gets the path inside the classpath where the input methods should be found 546 */ 547 public static String getIMBase(){return imBase;} 548 549 550 static private Font keyboardFont = new Font("Arial Unicode MS", Font.PLAIN, 12); 551 public static Font getKeyboardFont(){ 552 return keyboardFont; 553 } 554 }// class GateIM implements InputMethod 555
|
GateIM |
|