1   /*
2    * KeyboardMap.java
3    *
4    * Copyright (c) 1998-2005, 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: KeyboardMap.java,v 1.10 2005/01/11 13:51:38 ian Exp $
16   */
17  package guk.im;
18  
19  import java.awt.*;
20  import java.awt.event.*;
21  import java.util.*;
22  
23  import javax.swing.*;
24  import javax.swing.border.*;
25  
26  /**
27   * A virtual keyboard map. It uses its own thread do udate the display.
28   *
29   */
30  public class KeyboardMap implements Runnable{
31  
32    /**
33     * Builds the keyboard map. Uses a window provided by the context of the input
34     * method.
35     *
36     * @param im the input method
37     * @param handler the active Locale handler
38     * @param state the state of the handler.
39     */
40    public KeyboardMap(GateIM im, LocaleHandler handler, State state){
41      this.im = im;
42      this.handler = handler;
43      this.state = state;
44      jobs = Collections.synchronizedList(new ArrayList());
45      myThread = new Thread(Thread.currentThread().getThreadGroup(),
46                            this);
47      myThread.start();
48    }
49  
50    /**
51     * The run method for the thread responsible for updating the display.
52     */
53    public void run(){
54      //do all the initialisations
55      this.window = im.getContext().createInputMethodJFrame(null, true);
56      window.setTitle(handler.locale.getDisplayName() + " keyboard map");
57      window.setVisible(false);
58      window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
59      window.addComponentListener(new ComponentAdapter(){
60        public void componentHidden(ComponentEvent e){
61          window.dispose();
62        }
63      });
64      
65      window.getContentPane().setLayout(new GridLayout(1,1));
66      GridBagLayout layout = new GridBagLayout();
67      contentPane = new JPanel(layout);
68      contentPane.setDoubleBuffered(true);
69      window.getContentPane().add(contentPane, BorderLayout.CENTER);
70  
71      labelForKey = new HashMap();
72      GUIforString = new HashMap();
73  
74      double [] wheights = new double[45];
75      for(int i = 0; i < wheights.length; i++) wheights[i] = 0.001;
76      layout.columnWeights = wheights;
77      wheights = new double[5];
78      for(int i = 0; i < wheights.length; i++) wheights[i] = 0.001;
79      layout.rowWeights = wheights;
80  
81      //read keycaps
82      Map keyCap = handler.getKeyCap();
83      Iterator keyIter = keyCap.keySet().iterator();
84      Key currentKey;
85      JLabel currentLabel;
86      //LabelUI uLabelUI = new BasicUnicodeLabelUI(GateIM.getFontSet());
87      while(keyIter.hasNext()){
88        currentKey = (Key)keyIter.next();
89        currentLabel = new JLabel();
90        //currentLabel.setUI(uLabelUI);
91        currentLabel.setFont(GateIM.getKeyboardFont());
92        currentLabel.setForeground(Color.black);
93        currentLabel.setText((String)keyCap.get(currentKey));
94        labelForKey.put(currentKey, currentLabel);
95      }
96      //build the guis
97      GUIforString = new HashMap();
98      KeyGUI currentGui;
99      int currentModifiers = 0;
100     if(ctrl) currentModifiers |= InputEvent.CTRL_MASK;
101     if(alt) currentModifiers |= InputEvent.ALT_MASK;
102     char ch;
103     String sKey;
104     for(ch = 'a'; ch <= 'z'; ch++){
105       sKey = "" + ch;
106       if(shift) currentKey = new Key(Character.toUpperCase(ch), currentModifiers);
107       else currentKey = new Key(ch, currentModifiers);
108       currentGui = new KeyGUI(sKey, ch, Character.toUpperCase(ch),
109                              (JLabel)labelForKey.get(currentKey));
110       GUIforString.put(sKey, currentGui);
111     }
112     if(shift) ch = '!'; else ch = '1';
113     sKey = "1";
114     currentKey = new Key(ch, currentModifiers);
115     currentGui = new KeyGUI(sKey, '1', '!',(JLabel)labelForKey.get(currentKey));
116     GUIforString.put(sKey, currentGui);
117     if(shift) ch = '\"'; else ch = '2';
118     sKey = "2";
119     currentKey = new Key(ch, currentModifiers);
120     currentGui = new KeyGUI(sKey, '2', '\"',(JLabel)labelForKey.get(currentKey));
121     GUIforString.put(sKey, currentGui);
122     if(shift) ch = '\u00a3'; else ch = '3';  //pound symbol
123     sKey = "3";
124     currentKey = new Key(ch, currentModifiers);
125     currentGui = new KeyGUI(sKey, '3', '\u00a3',(JLabel)labelForKey.get(currentKey)); //pound symbol
126     GUIforString.put(sKey, currentGui);
127     if(shift) ch = '$'; else ch = '4';
128     sKey = "4";
129     currentKey = new Key(ch, currentModifiers);
130     currentGui = new KeyGUI(sKey, '4', '$',(JLabel)labelForKey.get(currentKey));
131     GUIforString.put(sKey, currentGui);
132     if(shift) ch = '%'; else ch = '5';
133     sKey = "5";
134     currentKey = new Key(ch, currentModifiers);
135     currentGui = new KeyGUI(sKey, '5', '%',(JLabel)labelForKey.get(currentKey));
136     GUIforString.put(sKey, currentGui);
137     if(shift) ch = '^'; else ch = '6';
138     sKey = "6";
139     currentKey = new Key(ch, currentModifiers);
140     currentGui = new KeyGUI(sKey, '6', '^',(JLabel)labelForKey.get(currentKey));
141     GUIforString.put(sKey, currentGui);
142     if(shift) ch = '&'; else ch = '7';
143     sKey = "7";
144     currentKey = new Key(ch, currentModifiers);
145     currentGui = new KeyGUI(sKey, '7', '&',(JLabel)labelForKey.get(currentKey));
146     GUIforString.put(sKey, currentGui);
147     if(shift) ch = '*'; else ch = '8';
148     sKey = "8";
149     currentKey = new Key(ch, currentModifiers);
150     currentGui = new KeyGUI(sKey, '8', '*',(JLabel)labelForKey.get(currentKey));
151     GUIforString.put(sKey, currentGui);
152     if(shift) ch = '('; else ch = '9';
153     sKey = "9";
154     currentKey = new Key(ch, currentModifiers);
155     currentGui = new KeyGUI(sKey, '9', '(',(JLabel)labelForKey.get(currentKey));
156     GUIforString.put(sKey, currentGui);
157     if(shift) ch = ')'; else ch = '0';
158     sKey = "0";
159     currentKey = new Key(ch, currentModifiers);
160     currentGui = new KeyGUI(sKey, '0', ')',(JLabel)labelForKey.get(currentKey));
161     GUIforString.put(sKey, currentGui);
162     if(shift) ch = '\u00ac'; else ch = '`';  //negation symbol
163     sKey = "`";
164     currentKey = new Key(ch, currentModifiers);
165     currentGui = new KeyGUI(sKey, '`', '\u00ac',(JLabel)labelForKey.get(currentKey)); //negation symbol
166     GUIforString.put(sKey, currentGui);
167     if(shift) ch = '_'; else ch = '-';
168     sKey = "-";
169     currentKey = new Key(ch, currentModifiers);
170     currentGui = new KeyGUI(sKey, '-', '_',(JLabel)labelForKey.get(currentKey));
171     GUIforString.put(sKey, currentGui);
172     if(shift) ch = '+'; else ch = '=';
173     sKey = "=";
174     currentKey = new Key(ch, currentModifiers);
175     currentGui = new KeyGUI(sKey, '=', '+',(JLabel)labelForKey.get(currentKey));
176     GUIforString.put(sKey, currentGui);
177     if(shift) ch = '{'; else ch = '[';
178     sKey = "[";
179     currentKey = new Key(ch, currentModifiers);
180     currentGui = new KeyGUI(sKey, '[', '{',(JLabel)labelForKey.get(currentKey));
181     GUIforString.put(sKey, currentGui);
182     if(shift) ch = '}'; else ch = ']';
183     sKey = "]";
184     currentKey = new Key(ch, currentModifiers);
185     currentGui = new KeyGUI(sKey, ']', '}',(JLabel)labelForKey.get(currentKey));
186     GUIforString.put(sKey, currentGui);
187     if(shift) ch = ':'; else ch = ';';
188     sKey = ";";
189     currentKey = new Key(ch, currentModifiers);
190     currentGui = new KeyGUI(sKey, ';', ':',(JLabel)labelForKey.get(currentKey));
191     GUIforString.put(sKey, currentGui);
192     if(shift) ch = '@'; else ch = '\'';
193     sKey = "'";
194     currentKey = new Key(ch, currentModifiers);
195     currentGui = new KeyGUI(sKey, '\'', '@',(JLabel)labelForKey.get(currentKey));
196     GUIforString.put(sKey, currentGui);
197     if(shift) ch = '~'; else ch = '#';
198     sKey = "#";
199     currentKey = new Key(ch, currentModifiers);
200     currentGui = new KeyGUI(sKey, '#', '~',(JLabel)labelForKey.get(currentKey));
201     GUIforString.put(sKey, currentGui);
202     if(shift) ch = '|'; else ch = '\\';
203     sKey = "\\";
204     currentKey = new Key(ch, currentModifiers);
205     currentGui = new KeyGUI(sKey, '\\', '|',(JLabel)labelForKey.get(currentKey));
206     GUIforString.put(sKey, currentGui);
207     if(shift) ch = '<'; else ch = ',';
208     sKey = ",";
209     currentKey = new Key(ch, currentModifiers);
210     currentGui = new KeyGUI(sKey, ',', '<',(JLabel)labelForKey.get(currentKey));
211     GUIforString.put(sKey, currentGui);
212     if(shift) ch = '>'; else ch = '.';
213     sKey = ".";
214     currentKey = new Key(ch, currentModifiers);
215     currentGui = new KeyGUI(sKey, '.', '>',(JLabel)labelForKey.get(currentKey));
216     GUIforString.put(sKey, currentGui);
217     if(shift) ch = '?'; else ch = '/';
218     sKey = "/";
219     currentKey = new Key(ch, currentModifiers);
220     currentGui = new KeyGUI(sKey, '/', '?',(JLabel)labelForKey.get(currentKey));
221     GUIforString.put(sKey, currentGui);
222 
223     GUIforString.put("BACK_SPACE", new KeyGUI("BACK_SPACE", (char)0,(char)0,new JLabel("BackSpace")));
224     GUIforString.put("TAB", new KeyGUI("TAB", (char)0,(char)0,new JLabel("Tab")));
225     GUIforString.put("CAPS_LOCK", new KeyGUI("CAPS_LOCK", (char)0,(char)0,new JLabel("Caps Lock")));
226     GUIforString.put("ENTER", new KeyGUI("ENTER", (char)0, (char)0,new JLabel("Enter")));
227     GUIforString.put("LSHIFT", new KeyGUI("LSHIFT", (char)0,(char)0,new JLabel("Shift")));
228     GUIforString.put("RSHIFT", new KeyGUI("RSHIFT", (char)0,(char)0,new JLabel("Shift")));
229     GUIforString.put("LCTRL", new KeyGUI("LCTRL", (char)0,(char)0,new JLabel("Ctrl")));
230     GUIforString.put("RCTRL", new KeyGUI("RCTRL", (char)0,(char)0,new JLabel("Ctrl")));
231     GUIforString.put("LALT", new KeyGUI("LALT", (char)0,(char)0,new JLabel("Alt")));
232     GUIforString.put("RALT", new KeyGUI("RALT", (char)0,(char)0,new JLabel("Alt")));
233     GUIforString.put("SPACE", new KeyGUI("SPACE", ' ', ' ',new JLabel(" ")));
234 
235     //add the components to the window
236     GridBagConstraints constraints = new GridBagConstraints();
237     constraints.fill = GridBagConstraints.BOTH;
238     constraints.gridwidth = 3;
239     contentPane.add((Component)GUIforString.get("`"), constraints);
240     contentPane.add((Component)GUIforString.get("1"), constraints);
241     contentPane.add((Component)GUIforString.get("2"), constraints);
242     contentPane.add((Component)GUIforString.get("3"), constraints);
243     contentPane.add((Component)GUIforString.get("4"), constraints);
244     contentPane.add((Component)GUIforString.get("5"), constraints);
245     contentPane.add((Component)GUIforString.get("6"), constraints);
246     contentPane.add((Component)GUIforString.get("7"), constraints);
247     contentPane.add((Component)GUIforString.get("8"), constraints);
248     contentPane.add((Component)GUIforString.get("9"), constraints);
249     contentPane.add((Component)GUIforString.get("0"), constraints);
250     contentPane.add((Component)GUIforString.get("-"), constraints);
251     contentPane.add((Component)GUIforString.get("="), constraints);
252     constraints.gridwidth = 6;
253     contentPane.add((Component)GUIforString.get("BACK_SPACE"), constraints);
254     //second line
255     constraints.gridy = 1;
256     constraints.gridwidth = 5;
257     contentPane.add((Component)GUIforString.get("TAB"), constraints);
258     constraints.gridwidth = 3;
259     contentPane.add((Component)GUIforString.get(""+'q'), constraints);
260     contentPane.add((Component)GUIforString.get(""+'w'), constraints);
261     contentPane.add((Component)GUIforString.get(""+'e'), constraints);
262     contentPane.add((Component)GUIforString.get(""+'r'), constraints);
263     contentPane.add((Component)GUIforString.get(""+'t'), constraints);
264     contentPane.add((Component)GUIforString.get(""+'y'), constraints);
265     contentPane.add((Component)GUIforString.get(""+'u'), constraints);
266     contentPane.add((Component)GUIforString.get(""+'i'), constraints);
267     contentPane.add((Component)GUIforString.get(""+'o'), constraints);
268     contentPane.add((Component)GUIforString.get(""+'p'), constraints);
269     contentPane.add((Component)GUIforString.get(""+'['), constraints);
270     contentPane.add((Component)GUIforString.get(""+']'), constraints);
271     constraints.gridwidth = 4;
272     contentPane.add((Component)GUIforString.get(""+'\\'), constraints);
273     //line 3
274     constraints.gridy = 2;
275     constraints.gridwidth = 6;
276     contentPane.add((Component)GUIforString.get("CAPS_LOCK"), constraints);
277     constraints.gridwidth = 3;
278     contentPane.add((Component)GUIforString.get("a"), constraints);
279     contentPane.add((Component)GUIforString.get(""+'s'), constraints);
280     contentPane.add((Component)GUIforString.get(""+'d'), constraints);
281     contentPane.add((Component)GUIforString.get(""+'f'), constraints);
282     contentPane.add((Component)GUIforString.get(""+'g'), constraints);
283     contentPane.add((Component)GUIforString.get(""+'h'), constraints);
284     contentPane.add((Component)GUIforString.get(""+'j'), constraints);
285     contentPane.add((Component)GUIforString.get(""+'k'), constraints);
286     contentPane.add((Component)GUIforString.get(""+'l'), constraints);
287     contentPane.add((Component)GUIforString.get(""+';'), constraints);
288     contentPane.add((Component)GUIforString.get("'"), constraints);
289     constraints.gridwidth = 6;
290     contentPane.add((Component)GUIforString.get("ENTER"), constraints);
291     //line 4
292     constraints.gridy = 3;
293     constraints.gridwidth = 5;
294     contentPane.add((Component)GUIforString.get("LSHIFT"), constraints);
295     constraints.gridwidth = 3;
296     contentPane.add((Component)GUIforString.get(""+'z'), constraints);
297     contentPane.add((Component)GUIforString.get(""+'x'), constraints);
298     contentPane.add((Component)GUIforString.get(""+'c'), constraints);
299     contentPane.add((Component)GUIforString.get(""+'v'), constraints);
300     contentPane.add((Component)GUIforString.get(""+'b'), constraints);
301     contentPane.add((Component)GUIforString.get(""+'n'), constraints);
302     contentPane.add((Component)GUIforString.get(""+'m'), constraints);
303     contentPane.add((Component)GUIforString.get(""+','), constraints);
304     contentPane.add((Component)GUIforString.get(""+'.'), constraints);
305     contentPane.add((Component)GUIforString.get(""+'/'), constraints);
306     contentPane.add((Component)GUIforString.get(""+'#'), constraints);
307     constraints.gridwidth = 8;
308     contentPane.add((Component)GUIforString.get("RSHIFT"), constraints);
309     //line 5
310     constraints.gridy = 4;
311     constraints.gridwidth = 5;
312     contentPane.add((Component)GUIforString.get("LCTRL"), constraints);
313     contentPane.add((Component)GUIforString.get("LALT"), constraints);
314     constraints.gridwidth = 25;
315     contentPane.add((Component)GUIforString.get("SPACE"), constraints);
316     constraints.gridwidth = 5;
317     contentPane.add((Component)GUIforString.get("RALT"), constraints);
318     contentPane.add((Component)GUIforString.get("RCTRL"), constraints);
319     window.pack();
320 
321     if(im.mapVisible) window.setVisible(true);
322 
323     //initialisations done
324     //wait for jobs to do
325     infinite:while(true){
326       synchronized(jobs){
327         try{
328           if(!jobs.isEmpty()){
329             //do the jobs in the job list
330             while(!jobs.isEmpty()){
331               Object job = jobs.remove(0);
332               //do job
333               if(job instanceof String){
334                 //job is a command
335                 String sJob = (String)job;
336                 //should we die? :(
337                 if(sJob.equalsIgnoreCase("DIE")) break infinite;
338                 else if(sJob.equalsIgnoreCase("HIDE") && window.isShowing()){
339                   window.setVisible(false);
340                 }else if(sJob.equalsIgnoreCase("SHOW") && (!window.isShowing())){
341                   window.setVisible(true);
342                 }else if(sJob.equalsIgnoreCase("UPDATE")) update();
343               }else if(job instanceof KeyEvent){
344                 //job is an key event
345                 int code;
346                 KeyEvent kEvent = (KeyEvent)job;
347                 KeyGUI someGui;
348                 if(kEvent.getID() == KeyEvent.KEY_PRESSED){
349                   code = kEvent.getKeyCode();
350                   switch(code){
351                     case KeyEvent.VK_SHIFT: {
352                       if(!shift) {
353                         shift = true;
354                         ((KeyGUI)GUIforString.get("LSHIFT")).pressKey();
355                         ((KeyGUI)GUIforString.get("RSHIFT")).pressKey();
356                         updateLabels();
357                       }
358                       break;
359                     }
360                     case KeyEvent.VK_CONTROL:{
361                       if(!ctrl){
362                         ctrl = true;
363                         ((KeyGUI)GUIforString.get("LCTRL")).pressKey();
364                         ((KeyGUI)GUIforString.get("RCTRL")).pressKey();
365                       updateLabels();
366                       }
367                       break;
368                     }
369                     case KeyEvent.VK_ALT:{
370                       if(!alt){
371                         alt = true;
372                         ((KeyGUI)GUIforString.get("LALT")).pressKey();
373                         ((KeyGUI)GUIforString.get("RALT")).pressKey();
374                         updateLabels();
375                       }
376                       break;
377                     }
378                     case KeyEvent.VK_CAPS_LOCK:{
379                       someGui = (KeyGUI)GUIforString.get("CAPS_LOCK");
380                       someGui.pressKey();
381                       break;
382                     }
383                     case KeyEvent.VK_BACK_SPACE:{
384                       someGui = (KeyGUI)GUIforString.get("BACK_SPACE");
385                       someGui.pressKey();
386                       break;
387                     }
388                     case KeyEvent.VK_ENTER:{
389                       someGui = (KeyGUI)GUIforString.get("ENTER");
390                       someGui.pressKey();
391                       break;
392                     }
393                     case KeyEvent.VK_TAB:{
394                       someGui = (KeyGUI)GUIforString.get("TAB");
395                       someGui.pressKey();
396                       break;
397                     }
398                     case KeyEvent.VK_SPACE:{
399                       someGui = (KeyGUI)GUIforString.get("SPACE");
400                       someGui.pressKey();
401                       break;
402                     }
403                     default:{
404                       String key = "";
405                       char keyCh = kEvent.getKeyChar();
406                       if('a' <= keyCh && keyCh <= 'z'){
407                         key += keyCh;
408                       }else if('A' <= keyCh && keyCh <= 'Z'){
409                         key += Character.toLowerCase(keyCh);
410                       }else{
411                         switch(keyCh){
412                           case '`':{
413                             key += keyCh;
414                             break;
415                           }
416                           case '1':{
417                             key += keyCh;
418                             break;
419                           }
420                           case '2':{
421                             key += keyCh;
422                             break;
423                           }
424                           case '3':{
425                             key += keyCh;
426                             break;
427                           }
428                           case '4':{
429                             key += keyCh;
430                             break;
431                           }
432                           case '5':{
433                             key += keyCh;
434                             break;
435                           }
436                           case '6':{
437                             key += keyCh;
438                             break;
439                           }
440                           case '7':{
441                             key += keyCh;
442                             break;
443                           }
444                           case '8':{
445                             key += keyCh;
446                             break;
447                           }
448                           case '9':{
449                             key += keyCh;
450                             break;
451                           }
452                           case '0':{
453                             key += keyCh;
454                             break;
455                           }
456                           case '-':{
457                             key += keyCh;
458                             break;
459                           }
460                           case '=':{
461                             key += keyCh;
462                             break;
463                           }
464                           case '[':{
465                             key += keyCh;
466                             break;
467                           }
468                           case ']':{
469                             key += keyCh;
470                             break;
471                           }
472                           case ';':{
473                             key += keyCh;
474                             break;
475                           }
476                           case '\'':{
477                             key += keyCh;
478                             break;
479                           }
480                           case '#':{
481                             key += keyCh;
482                             break;
483                           }
484                           case '\\':{
485                             key += keyCh;
486                             break;
487                           }
488                           case ',':{
489                             key += keyCh;
490                             break;
491                           }
492                           case '.':{
493                             key += keyCh;
494                             break;
495                           }
496                           case '/':{
497                             key += keyCh;
498                             break;
499                           }
500 
501 
502                           case '\u00ac':{ //negation symbol
503                             key += '`';
504                             break;
505                           }
506                           case '!':{
507                             key += '1';
508                             break;
509                           }
510                           case '\"':{
511                             key += '2';
512                             break;
513                           }
514                           case '\u00a3':{ //pound symbol
515                             key += '3';
516                             break;
517                           }
518                           case '$':{
519                             key += '4';
520                             break;
521                           }
522                           case '%':{
523                             key += '5';
524                             break;
525                           }
526                           case '^':{
527                             key += '6';
528                             break;
529                           }
530                           case '&':{
531                             key += '7';
532                             break;
533                           }
534                           case '*':{
535                             key += '8';
536                             break;
537                           }
538                           case '(':{
539                             key += '9';
540                             break;
541                           }
542                           case ')':{
543                             key += '0';
544                             break;
545                           }
546                           case '_':{
547                             key += '-';
548                             break;
549                           }
550                           case '+':{
551                             key += '=';
552                             break;
553                           }
554                           case '{':{
555                             key += '[';
556                             break;
557                           }
558                           case '}':{
559                             key += ']';
560                             break;
561                           }
562                           case ':':{
563                             key += ';';
564                             break;
565                           }
566                           case '@':{
567                             key += '\'';
568                             break;
569                           }
570                           case '~':{
571                             key += '#';
572                             break;
573                           }
574                           case '|':{
575                             key += '\\';
576                             break;
577                           }
578                           case '<':{
579                             key += ',';
580                             break;
581                           }
582                           case '>':{
583                             key += '.';
584                             break;
585                           }
586                           case '?':{
587                             key += '/';
588                             break;
589                           }
590                         }//switch
591                       }
592                       someGui = (KeyGUI)GUIforString.get(key);
593                       if(someGui != null){
594                         someGui.pressKey();
595                       }
596                     }//default
597                   }//switch
598                 }else if(kEvent.getID() == KeyEvent.KEY_RELEASED){
599                   code = kEvent.getKeyCode();
600                   switch(code){
601                     case KeyEvent.VK_SHIFT: {
602                       if(shift) {
603                         shift = false;
604                         ((KeyGUI)GUIforString.get("LSHIFT")).releaseKey();
605                         ((KeyGUI)GUIforString.get("RSHIFT")).releaseKey();
606                         updateLabels();
607                       }
608                       break;
609                     }
610                     case KeyEvent.VK_CONTROL:{
611                       if(ctrl){
612                         ctrl = false;
613                         ((KeyGUI)GUIforString.get("LCTRL")).releaseKey();
614                         ((KeyGUI)GUIforString.get("RCTRL")).releaseKey();
615                         updateLabels();
616                       }
617                       break;
618                     }
619                     case KeyEvent.VK_ALT:{
620                       if(alt){
621                         alt = false;
622                         ((KeyGUI)GUIforString.get("LALT")).releaseKey();
623                         ((KeyGUI)GUIforString.get("RALT")).releaseKey();
624                         updateLabels();
625                       }
626                       break;
627                     }
628                     case KeyEvent.VK_CAPS_LOCK:{
629                       someGui = (KeyGUI)GUIforString.get("CAPS_LOCK");
630                       someGui.releaseKey();
631                       break;
632                     }
633                     case KeyEvent.VK_BACK_SPACE:{
634                       someGui = (KeyGUI)GUIforString.get("BACK_SPACE");
635                       someGui.releaseKey();
636                       break;
637                     }
638                     case KeyEvent.VK_ENTER:{
639                       someGui = (KeyGUI)GUIforString.get("ENTER");
640                       someGui.releaseKey();
641                       break;
642                     }
643                     case KeyEvent.VK_TAB:{
644                       someGui = (KeyGUI)GUIforString.get("TAB");
645                       someGui.releaseKey();
646                       break;
647                     }
648                     case KeyEvent.VK_SPACE:{
649                       someGui = (KeyGUI)GUIforString.get("SPACE");
650                       someGui.releaseKey();
651                       break;
652                     }
653                     default:{
654                       String key = "";
655                       char keyCh = kEvent.getKeyChar();
656                       if('a' <= keyCh && keyCh <= 'z'){
657                         key += keyCh;
658                       }else if('A' <= keyCh && keyCh <= 'Z'){
659                         key += Character.toLowerCase(keyCh);
660                       }else{
661                         switch(keyCh){
662                           case '`':{
663                             key += keyCh;
664                             break;
665                           }
666                           case '1':{
667                             key += keyCh;
668                             break;
669                           }
670                           case '2':{
671                             key += keyCh;
672                             break;
673                           }
674                           case '3':{
675                             key += keyCh;
676                             break;
677                           }
678                           case '4':{
679                             key += keyCh;
680                             break;
681                           }
682                           case '5':{
683                             key += keyCh;
684                             break;
685                           }
686                           case '6':{
687                             key += keyCh;
688                             break;
689                           }
690                           case '7':{
691                             key += keyCh;
692                             break;
693                           }
694                           case '8':{
695                             key += keyCh;
696                             break;
697                           }
698                           case '9':{
699                             key += keyCh;
700                             break;
701                           }
702                           case '0':{
703                             key += keyCh;
704                             break;
705                           }
706                           case '-':{
707                             key += keyCh;
708                             break;
709                           }
710                           case '=':{
711                             key += keyCh;
712                             break;
713                           }
714                           case '[':{
715                             key += keyCh;
716                             break;
717                           }
718                           case ']':{
719                             key += keyCh;
720                             break;
721                           }
722                           case ';':{
723                             key += keyCh;
724                             break;
725                           }
726                           case '\'':{
727                             key += keyCh;
728                             break;
729                           }
730                           case '#':{
731                             key += keyCh;
732                             break;
733                           }
734                           case '\\':{
735                             key += keyCh;
736                             break;
737                           }
738                           case ',':{
739                             key += keyCh;
740                             break;
741                           }
742                           case '.':{
743                             key += keyCh;
744                             break;
745                           }
746                           case '/':{
747                             key += keyCh;
748                             break;
749                           }
750 
751                           case '\u00ac':{ //negation symbol
752                             key += '`';
753                             break;
754                           }
755                           case '!':{
756                             key += '1';
757                             break;
758                           }
759                           case '\"':{
760                             key += '2';
761                             break;
762                           }
763                           case '\u00a3':{ //pound symbol
764                             key += '3';
765                             break;
766                           }
767                           case '$':{
768                             key += '4';
769                             break;
770                           }
771                           case '%':{
772                             key += '5';
773                             break;
774                           }
775                           case '^':{
776                             key += '6';
777                             break;
778                           }
779                           case '&':{
780                             key += '7';
781                             break;
782                           }
783                           case '*':{
784                             key += '8';
785                             break;
786                           }
787                           case '(':{
788                             key += '9';
789                             break;
790                           }
791                           case ')':{
792                             key += '0';
793                             break;
794                           }
795                           case '_':{
796                             key += '-';
797                             break;
798                           }
799                           case '+':{
800                             key += '=';
801                             break;
802                           }
803                           case '{':{
804                             key += '[';
805                             break;
806                           }
807                           case '}':{
808                             key += ']';
809                             break;
810                           }
811                           case ':':{
812                             key += ';';
813                             break;
814                           }
815                           case '@':{
816                             key += '\'';
817                             break;
818                           }
819                           case '~':{
820                             key += '#';
821                             break;
822                           }
823                           case '|':{
824                             key += '\\';
825                             break;
826                           }
827                           case '<':{
828                             key += ',';
829                             break;
830                           }
831                           case '>':{
832                             key += '.';
833                             break;
834                           }
835                           case '?':{
836                             key += '/';
837                             break;
838                           }
839                         }//switch
840                       }
841                       someGui = (KeyGUI)GUIforString.get(key);
842                       if(someGui != null){
843                         someGui.releaseKey();
844                       }
845                     }//default
846                   }//switch
847                 }
848                 //update the state so the rebuildGui will update the highlights
849 //                state = im.currentState;
850 //                update();
851               }
852             }
853           }
854         }catch(Exception e){}
855         jobs.notifyAll();
856       }//synchronized(jobs);
857       //no more jobs, take a nap :)
858       try{
859         Thread.sleep(150);
860       }catch(InterruptedException ie){
861         ie.printStackTrace();
862       }
863     }//infinite: while(true)
864 
865   }
866 
867   /**
868    * Adds a job to the job list of the thread.
869    * A job is either a {@link java.lang.String} or an {@link java.awt.event.InputEvent}
870    * The string can be one of
871    * <ul>
872    * <li>SHOW: shows the keyboard map window
873    * <li>UPDATE updates the keyboard map window
874    * <li>HIDE: hides the keyboard map window
875    * <li>DIE: releases all the memory and terminates the thread
876    * </ul>
877    *
878    * The input events refer to pressed keys and are treated accordingly.
879    *
880    * @param job
881    */
882   public void addJob(Object job){
883     synchronized(jobs){
884       jobs.add(job);
885       jobs.notifyAll();
886     }
887   }
888 
889   /**
890    * Updates the keyboard map for a new Locale or a new state of the current locale handler.
891    * Currently the state changes are ignored.
892    * This method delegates its job to the thread which will do the actual
893    * update.
894    * @param newHandler
895    * @param newState
896    */
897   public void update(LocaleHandler newHandler, State newState){
898     //did anything changed?
899     if(newHandler == handler && newState == state) return;
900     this.newHandler = newHandler;
901     this.newState = newState;
902     addJob("UPDATE");
903   }
904 
905   /**
906    * Does th actual update.
907    */
908   protected void update(){
909     //did the locale changed?
910     if(newHandler != handler){
911       handler = newHandler;
912       state = newState;
913       window.setTitle(handler.locale.getDisplayLanguage() + " (" +
914                       handler.locale.getVariant() + ") keyboard map");
915       //read keycaps
916       labelForKey.clear();
917       Map keyCap = handler.getKeyCap();
918       Iterator keyIter = keyCap.keySet().iterator();
919       Key currentKey;
920       JLabel currentLabel;
921       //LabelUI uLabelUI = new BasicUnicodeLabelUI(GateIM.getFontSet());
922       while(keyIter.hasNext()){
923         currentKey = (Key)keyIter.next();
924         currentLabel = new JLabel();
925         currentLabel.setFont(GateIM.getKeyboardFont());
926         //currentLabel.setUI(uLabelUI);
927         currentLabel.setText((String)keyCap.get(currentKey));
928         labelForKey.put(currentKey, currentLabel);
929       }
930       updateLabels();
931     }
932     //did the state changed?
933     if(newState != state){
934       //highlight the allowed keys
935       state = newState;
936       //un-highlight the highlighted keys
937       Iterator keysIter = highlightedKeys.iterator();
938       while(keysIter.hasNext()) ((KeyGUI)keysIter.next()).unHighlight();
939       highlightedKeys.clear();
940 
941       //if not initial state highlight the allowed keys
942       if(state != handler.getInitialState()){
943         keysIter = state.transitionFunction.keySet().iterator();
944         KeyGUI someGui;
945         while(keysIter.hasNext()){
946           someGui = guiForKey((Key)keysIter.next());
947           if(someGui != null){
948             someGui.highlight();
949             highlightedKeys.add(someGui);
950           }
951         }
952       }
953     }
954   }
955 
956   /**
957    * Updates the virtual keyboard to reflect the current state.
958    */
959   protected void updateLabels(){
960     //update the labels
961     Component[] components = contentPane.getComponents();
962     for(int i = 0; i <components.length; i++){
963       if(components[i] instanceof KeyGUI) ((KeyGUI)components[i]).updateLabel();
964     }
965     fixShape();
966   }
967 
968   /**    */
969   protected void fixShape(){
970     //get the current sizes
971     int [][] sizes = ((GridBagLayout)contentPane.getLayout()).getLayoutDimensions();
972     //override the minimum sizes
973     ((GridBagLayout)contentPane.getLayout()).columnWidths = sizes[0];
974     ((GridBagLayout)contentPane.getLayout()).rowHeights = sizes[1];
975     window.pack();
976     contentPane.repaint(100);
977   }
978   /**
979    * Gets the gui that corresponds to a Key object.
980    *
981    * @param key
982    */
983   protected KeyGUI guiForKey(Key key){
984     char ch = key.keyChar;
985     boolean shiftOn = false;
986     if(Character.isUpperCase(ch)){
987       ch = Character.toLowerCase(ch);
988       shiftOn = true;
989     }
990     boolean ctrlOn = (key.modifiers & KeyEvent.CTRL_MASK) > 0;
991     boolean altOn = (key.modifiers & KeyEvent.ALT_MASK) > 0;
992     if(shift == shiftOn &&
993        ctrl == ctrlOn &&
994        alt == altOn){
995       return (KeyGUI)GUIforString.get("" + ch);
996     }
997     return null;
998   }
999   /**
1000   * Is the Shift key pressed?
1001   *
1002   * @param shift
1003   */
1004  public void setShift(boolean shift){
1005    this.shift = shift;
1006  }
1007
1008  /**
1009   * Is the Alt key pressed?
1010   *
1011   * @param alt
1012   */
1013  public void setAlt(boolean alt){
1014    this.alt = alt;
1015  }
1016
1017  /**
1018   * Is the Ctrl key pressed?
1019   *
1020   * @param ctrl
1021   */
1022  public void setCtrl(boolean ctrl){
1023    this.ctrl = ctrl;
1024  }
1025
1026//variables
1027
1028  //the current LocaleHandler
1029  /**
1030   * the active locale handler
1031   */
1032  LocaleHandler handler;
1033  //thenew handler that will become current on the next update
1034  /**
1035   * The new active locale handler. This member is useful during the period when the active locale handler has changed but the keyboard map is not updated yet. The keyboard map will be updated as soon as the tasks that were added to the job list before the locale change are consumed.
1036   */
1037  LocaleHandler newHandler;
1038
1039  //the current state of the current locale handler
1040  /**
1041   * The current state of the current locale handler.
1042   */
1043  State state;
1044  //the new state that will become current on the next update
1045  /**
1046   * The current state of the new current locale handler.
1047   *
1048   * @see #newHandler
1049   */
1050  State newState;
1051  /**
1052   * The window used for displaying the keyboard map
1053   */
1054  JFrame window;
1055  /**
1056   * The content pane that holds all the KeyGUIs.
1057   *
1058   * @see guk.im.KeyboardMap.KeyGUI
1059   */
1060  JPanel contentPane;
1061
1062  /**
1063   * The keys curently highlighted
1064   */
1065  java.util.List highlightedKeys = new ArrayList();
1066
1067  /**    */
1068  boolean shift = false, ctrl = false, alt = false, capslock = false;
1069  /** maps from String(the English lowercase representation of the key) to
1070   * KeyGUI
1071   */
1072  Map GUIforString;
1073  //maps from Key to JLabel for the key that have keyCap defined
1074  /**
1075   * Maps from Key to JLabel for the keys that have keyCap defined
1076   * .
1077   */
1078  Map labelForKey;
1079
1080  /**
1081   * The input method.
1082   */
1083  GateIM im;
1084
1085  /**
1086   * The thread that does the updating.
1087   */
1088  Thread myThread;
1089
1090  /**
1091   * The job list.
1092   */
1093  java.util.List jobs;
1094//classes
1095  public class KeyGUI extends JPanel {
1096    /**      */
1097    Box leftBox;
1098    /**      */
1099    JLabel leftUpLabel = new JLabel();
1100    /**      */
1101    JLabel leftDownLabel = new JLabel();
1102    /**      */
1103    Component centerLabel;
1104    /**      */
1105    char low, up;
1106    /**      */
1107    Border normalBorder, highlightedBorder;
1108
1109    /**
1110     * Constructs a new KeyGUI.
1111     *
1112     * @param key the String key used in the map that holds the GUIs used for
1113     * keys
1114     * @param englishLow the English char on the key
1115     * @param englishUp the English char on the key when used with the Shift
1116     *     key.
1117     * @param center the center label (the Unicode character on the key)
1118     */
1119    public KeyGUI(String key, char englishLow, char englishUp,
1120                  JLabel center) {
1121      this.setBackground(Color.lightGray);
1122      low = englishLow;
1123      up = englishUp;
1124      leftBox = Box.createVerticalBox();
1125      Dimension dim;
1126      if(englishUp > (char)0){
1127        leftUpLabel.setFont(leftUpLabel.getFont().deriveFont((float)10));
1128        leftUpLabel.setText("" + englishUp);
1129        leftBox.add(leftUpLabel);
1130      }else{
1131        leftBox.add(placeHolder);
1132      }
1133      if(englishLow > (char)0){
1134        leftDownLabel.setFont(leftDownLabel.getFont().deriveFont((float)10));
1135        leftDownLabel.setText("" + englishLow);
1136        leftBox.add(leftDownLabel);
1137      }else{
1138        leftBox.add(placeHolder);
1139      }
1140      leftBox.add(Box.createVerticalGlue());
1141      if(center == null) centerLabel = placeHolder;
1142      else centerLabel = center;
1143      this.setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
1144      this.add(leftBox);
1145      this.add(Box.createHorizontalGlue());
1146      this.add(centerLabel);
1147      normalBorder = new CompoundBorder(new BevelBorder(BevelBorder.RAISED),
1148                                        new EmptyBorder(2,3,2,3));
1149      highlightedBorder = new CompoundBorder(new BevelBorder(BevelBorder.RAISED),
1150                                        new MatteBorder(2,3,2,3, Color.green));
1151
1152      this.setBorder(normalBorder);
1153      addMouseListener(new MouseAdapter(){
1154        /**          *
1155         * @param e
1156         */
1157        public void mouseClicked(MouseEvent e){
1158          int modifiers = 0;
1159          if(ctrl) modifiers |= KeyEvent.CTRL_MASK;
1160          if(alt) modifiers |= KeyEvent.ALT_MASK;
1161          char ch;
1162          if(shift) ch = up;
1163          else ch = low;
1164          if(ch != 0){
1165            im.dispatchEvent(new KeyEvent(window, KeyEvent.KEY_TYPED,
1166                                          System.currentTimeMillis(),
1167                                          modifiers, KeyEvent.VK_UNDEFINED, ch));
1168          }
1169        }
1170        /**          */
1171        public void mousePressed(MouseEvent e){
1172          char ch;
1173          if(shift) ch = up;
1174          else ch = low;
1175          if(ch != 0){
1176            pressKey();
1177            //repaint(100);
1178          }
1179        }
1180        /**          */
1181        public void mouseReleased(MouseEvent e){
1182          char ch;
1183          if(shift) ch = up;
1184          else ch = low;
1185          if(ch != 0){
1186            releaseKey();
1187            //repaint(100);
1188          }
1189        }
1190      });
1191    }
1192
1193    /**      */
1194    public void updateLabel(){
1195      if(low == (char)0 || up == (char)0)return;
1196      remove(centerLabel);
1197      Key key;
1198      int modifiers = 0;
1199      if(ctrl) modifiers |= InputEvent.CTRL_MASK;
1200      if(alt) modifiers |= InputEvent.ALT_MASK;
1201      if(shift) key = new Key(up, modifiers);
1202      else key = new Key(low, modifiers);
1203      centerLabel = (JLabel)labelForKey.get(key);
1204      if(centerLabel == null) centerLabel = placeHolder;
1205      this.add(centerLabel);
1206//      this.invalidate();
1207    }
1208
1209    /**
1210     * Displays this key as pressed
1211     */
1212    public void pressKey(){
1213      this.setBackground(Color.darkGray);
1214    }
1215
1216    /**
1217     * Displays ths key as released.
1218     */
1219    public void releaseKey(){
1220      this.setBackground(Color.lightGray);
1221    }
1222
1223    /**
1224     * Renders this KeyGUI as highlighted
1225     */
1226    public void highlight(){
1227      setBorder(highlightedBorder);
1228    }
1229
1230    /**
1231     * Renders this KeyGUI normaly (not highlighted)
1232     */
1233    public void unHighlight(){
1234      setBorder(normalBorder);
1235    }
1236
1237  }//public class KeyGUI extends JPanel
1238
1239
1240
1241  /**
1242   * Empty component used for the key that are not bound to a Unicode character.
1243   */
1244  static Component placeHolder = Box.createRigidArea(new Dimension(12, 12));
1245}//class KeyboardMap
1246