| 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 &quot;guk/im/data/&quot;
    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 |