KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > SnowMailClient > keyboard > KeyboardDialog


1 package SnowMailClient.keyboard;
2
3 import snow.utils.gui.Icons;
4 import SnowMailClient.Language.*;
5 import snow.lookandfeel.ThemesManager;
6
7 import java.awt.*;
8 import java.awt.event.*;
9 import javax.swing.*;
10 import javax.swing.border.*;
11 import javax.swing.event.*;
12 import java.util.*;
13 import javax.swing.text.*;
14
15 /** add a keyboard to enter text in a textpane.
16     The textpane document inserts are filtered and matching typed keyboard shortcuts are replaced.
17     IMPORTANT: The paste contents are not replaced. only types.
18 */

19 public final class KeyboardDialog extends JDialog
20 {
21   private final GridBagLayout gridBag = new GridBagLayout();
22   private final GridBagConstraints constr = new GridBagConstraints();
23   private int actualCol = 0;
24   private int numberOfCols = 2;
25   private int nButtons = 0;
26   private KeyboardMap map;
27   private final JLabel completionLabel = new JLabel(Language.translate("Completions: none"));
28   private final JPanel keyboardPanel = new JPanel();
29   private final JSpinner spinner = new JSpinner(new SpinnerNumberModel(11,1,99,1));
30
31   private final JComboBox keyboardsCB = new JComboBox(new Object JavaDoc[]{
32       new PolishMap(),
33       new RussianMap(),
34       new LituanianMap(),
35      // new HiraganaMap(),
36
new ThaiKeyboard()
37   });
38
39   private final Vector<KeyboardListener> listeners = new Vector<KeyboardListener>();
40
41   final String JavaDoc title;
42
43   public KeyboardDialog(JFrame parent)
44   {
45     super(parent, Language.translate("Keyboard"), false);
46     this.title = getTitle();
47     this.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
48     this.setDefaultLookAndFeelDecorated(true);
49     this.setFocusableWindowState(false);
50
51     this.getContentPane().setLayout(new BorderLayout());
52     this.getContentPane().add(completionLabel, BorderLayout.SOUTH);
53     this.getContentPane().add(keyboardPanel, BorderLayout.CENTER);
54
55     JPanel controlPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
56     controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));
57     controlPanel.setBorder(new EmptyBorder(3,2,3,2));
58
59     this.getContentPane().add(controlPanel, BorderLayout.NORTH);
60     controlPanel.add(keyboardsCB);
61     controlPanel.add(Box.createHorizontalGlue());
62     controlPanel.add(new JLabel(Language.translate("Size")+": "));
63     controlPanel.add(spinner);
64
65     keyboardsCB.setPreferredSize(new Dimension(
66       (int) keyboardsCB.getPreferredSize().getWidth(),
67        UIManager.getFont("Label.font").getSize()));
68
69     final JButton editButton = new JButton(Language.translate("Edit"));
70     editButton.setFont(ThemesManager.getInstance().getSmallFont());
71     controlPanel.add(Box.createHorizontalGlue());
72     //NOT FINISHED !!!controlPanel.add(editButton);
73
editButton.addActionListener(new ActionListener()
74     {
75        public void actionPerformed(ActionEvent ae)
76        {
77           JPopupMenu popup = new JPopupMenu("edit");
78           JMenuItem miEdit = new JMenuItem( Language.translate("Edit keyboard") );
79           popup.add(miEdit);
80           miEdit.addActionListener(new ActionListener()
81           {
82             public void actionPerformed(ActionEvent ae)
83             {
84                KeyboardEditor ke = new KeyboardEditor(KeyboardDialog.this);
85             }
86           });
87
88           JMenuItem miAdd = new JMenuItem( Language.translate("Add new keyboard"), Icons.PlusIcon.shared10 );
89           popup.add(miAdd);
90           miAdd.addActionListener(new ActionListener()
91           {
92             public void actionPerformed(ActionEvent ae)
93             {
94                 String JavaDoc name = JOptionPane.showInputDialog(editButton, Language.translate("Keyboard name: "));
95                 if(name!=null)
96                 {
97                     KeyboardMap kmap = new KeyboardMap(name, 8);
98                     setKeyboard(kmap);
99                 }
100             }
101           });
102           JMenuItem miRemove = new JMenuItem( Language.translate("Remove selected keyboard") );
103           //popup.add(miRemove);
104
miRemove.addActionListener(new ActionListener()
105           {
106             public void actionPerformed(ActionEvent ae)
107             {
108             }
109           });
110
111           popup.show(editButton,10,5);
112        }
113     });
114
115     keyboardsCB.addActionListener(new ActionListener()
116     {
117        public void actionPerformed(ActionEvent ae)
118        {
119           setKeyboard( (KeyboardMap) keyboardsCB.getSelectedItem() );
120           pack();
121        }
122     });
123
124     spinner.addChangeListener(new ChangeListener()
125     {
126        public void stateChanged(ChangeEvent ce)
127        {
128           int fs = Integer.parseInt(""+spinner.getValue());
129           Font font = new Font("LucidaSansRegular", Font.PLAIN, fs);
130           for(KeyboardKey k : map.getKeys())
131           {
132              if(k.button!=null)
133              {
134                k.button.setFont(font);
135              }
136           }
137           pack();
138        }
139     });
140
141     spinner.setPreferredSize(new Dimension(
142       (int) spinner.getPreferredSize().getWidth(),
143       (int) keyboardsCB.getPreferredSize().getHeight()));
144
145
146     // otherwise, we can make it to disapear !
147
// ### NOT WORKING !!!
148
this.setMinimumSize(new Dimension(30,30));
149
150   } // Constructor
151

152   public KeyboardMap getActualKeyboardMap() { return map; }
153
154   public void setKeyboard(KeyboardMap map)
155   {
156     this.map = map;
157
158     numberOfCols = map.getColumnCount();
159     nButtons = 0;
160     actualCol = 0;
161     setTitle(map.getName());
162
163     keyboardPanel.removeAll();
164     keyboardPanel.setLayout(gridBag);
165
166     for(KeyboardKey key: map.getKeys())
167     {
168        addButton(key);
169     }
170   }
171
172   public String JavaDoc getCharsetForDisplayedKeyboard()
173   {
174     return map.getCharset();
175   }
176
177   public void addListener(KeyboardListener kl)
178   {
179     listeners.add(kl);
180   }
181
182   public void removeListener(KeyboardListener kl)
183   {
184     listeners.remove(kl);
185   }
186
187   /** set the pane where this keyboard writes
188   */

189   public void setTarget(final JTextPane pane)
190   {
191     if(!pane.isEditable()) return;
192
193     final DefaultStyledDocument doc = (DefaultStyledDocument) pane.getDocument();
194
195     // add this keyboard hits and write them in the pane
196
//
197
this.addListener(new KeyboardListener()
198     {
199       public void typed(String JavaDoc text)
200       {
201         try
202         {
203           // keys inserts are also made with replace instead of insert
204
//int lsel = pane.getSelectionEnd()-pane.getSelectionStart();
205
//if(lsel<0) lsel=0;
206
//doc.replace(pane.getCaretPosition(), lsel, text, null);
207

208           doc.insertString(pane.getCaretPosition(), text, null);
209         }
210         catch(Exception JavaDoc ex)
211         {
212           ex.printStackTrace();
213         }
214       }
215     });
216
217     // filter the key inserts in the pane...
218
//
219
doc.setDocumentFilter(new DocumentFilter()
220     {
221        public void insertString(DocumentFilter.FilterBypass fb, int offset, String JavaDoc string, AttributeSet attr) throws BadLocationException
222        {
223           //System.out.println("Filter insert: "+string+" at "+offset);
224
fb.insertString(offset, string, attr);
225           resetHighlights();
226        }
227
228        public void remove(DocumentFilter.FilterBypass fb, int offset, int length) throws BadLocationException
229        {
230           //System.out.println("Filter remove: at "+offset+", l="+length);
231
fb.remove(offset, length);
232           resetHighlights();
233        }
234
235        public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String JavaDoc text, AttributeSet attrs) throws BadLocationException
236        {
237           //System.out.println("Filter replace: "+text+" at "+offset+", l="+length);
238
filterReplace(fb, offset, length, text, attrs);
239        }
240
241     });
242
243     // listen to the ESC key hit
244
//
245
pane.addKeyListener(new KeyAdapter()
246     {
247       @Override JavaDoc public void keyPressed(KeyEvent ke)
248       {
249         if(ke.getKeyCode()==KeyEvent.VK_ESCAPE)
250         {
251           //System.out.println("ESCAPE PRESSED !");
252
completionBuffer.resetBuffer();
253           resetHighlights();
254         }
255       }
256     });
257   }
258
259
260
261   /** this is the actual match buffer. These chars are to be treated
262   */

263   //final private StringBuffer matchBuffer = new StringBuffer();
264
final private CompletionBuffer completionBuffer = new CompletionBuffer();
265
266   int previousReplaceOffset=-1;
267
268   /** the user types call this.
269       the user CTRL+V call this
270       the keyboard clicks DOESN't call this.
271       only replace matching shortcuts entered with single characters at once.
272   */

273   private void filterReplace(DocumentFilter.FilterBypass fb, int replaceOffset, int replaceLength, String JavaDoc text, AttributeSet attrs) throws BadLocationException
274   {
275     //System.out.println("FR offset="+replaceOffset+", l="+replaceLength+", text="+text);
276

277     // always write the actual typed text.
278
// we make the eventual replacements later
279
fb.replace(replaceOffset, replaceLength, text, attrs);
280
281     // accept only single typed chars !!!
282
// don't convert on pastes...
283
if(text.length()>1) return;
284
285     // the position where the blink NOW is
286
int offset = replaceOffset+text.length();
287
288     this.completionLabel.setText(Language.translate("Completions: none"));
289
290     // replace ONLY if visible
291
if(!this.isVisible()) return;
292
293     resetHighlights();
294
295
296     // reset the buffer if the actual insert is not next to previous
297
if(previousReplaceOffset==-1)
298     {
299       previousReplaceOffset = offset-1;
300     }
301
302     if( completionBuffer.getBufferLength()>0 // has precedent chars
303
&& previousReplaceOffset!=-1 // written at another place than
304
&& offset-previousReplaceOffset!=1) // the last one
305
{
306       // reset the buffer, because we have disjoint char positions !
307
System.out.println("Reset buffer because offset jump : "+previousReplaceOffset+" "+offset);
308       completionBuffer.resetBuffer();
309       // ### complete the buffer ?
310
// continue with the single new char
311
}
312     previousReplaceOffset = offset;
313
314
315     // add the new typed text in the buffer
316
//
317
completionBuffer.addChars(text);
318
319     // this is the complete shortcut
320
String JavaDoc typedShortcut = completionBuffer.toString();
321
322     // and the keys starting with it
323
Vector<KeyboardKey> keys = map.getKeyStartingWithShortcut(typedShortcut);
324
325
326     if(keys.size()==0)
327     {
328       // no more candidates, bad shortcut {a,b,c}
329
completionBuffer.resetBuffer();
330
331       // 1) look if {a,b} or {a} are matching
332
for(int end=typedShortcut.length()-1; end>=1; end--)
333       {
334          String JavaDoc sub = typedShortcut.substring(0,end);
335          //System.out.println("try "+sub);
336
// is it an exact matching ?
337
KeyboardKey matchingKey = map.getKeyWithShortcut(sub);
338          if(matchingKey!=null)
339          {
340             String JavaDoc toRemove = sub;
341             int pos = offset-toRemove.length() -typedShortcut.length() + end ;
342             //System.out.println("Removing "+toRemove+" at pos "+pos);
343

344             fb.remove(pos, toRemove.length());
345
346             fb.replace(pos, 0, matchingKey.toString(), attrs);
347
348             // we have a rest for a subsequent completion !
349
completionBuffer.addChars(typedShortcut.substring(end));
350             //System.out.println("Rest: "+typedShortcut.substring(end));
351
// direct evaluate recursively for completion
352
// ###
353
highlightKeysStartingWith(completionBuffer.toString());
354
355             matchingKey = map.getKeyWithShortcut(completionBuffer.toString());
356             if(matchingKey!=null)
357             {
358                System.out.println("match !");
359             }
360
361             return;
362          }
363       }
364
365
366       // 2) look if {b,c} or {c} are good candidates
367
for(int start=1; start<typedShortcut.length(); start++)
368       {
369         String JavaDoc sub = typedShortcut.substring(start);
370         //System.out.println("Trying "+sub);
371
Vector<KeyboardKey> nkeys = map.getKeyStartingWithShortcut(sub);
372         if(nkeys.size()>0)
373         {
374           completionBuffer.addChars(sub);
375           // => highlight the candidate(s)
376
setCandidates(nkeys);
377           return;
378         }
379       }
380
381
382
383     }
384     else if(keys.size()==1)
385     {
386       // is it an exact matching ?
387
KeyboardKey matchingKey = map.getKeyWithShortcut(typedShortcut);
388       if(matchingKey!=null)
389       {
390         // we have a matching !
391
// all the waiting keys are replaced with the key
392
String JavaDoc key = matchingKey.toString();
393
394         // remove all typed chars
395
String JavaDoc toRemove = completionBuffer.toString();
396         int pos = offset-toRemove.length();
397         //System.out.println("Remove "+toRemove+" at "+pos);
398
fb.remove(pos, toRemove.length());
399
400         // add the key at pos and don't replace anything (l=0)
401
fb.replace(pos, 0, key, attrs);
402         completionBuffer.resetBuffer();
403         return;
404       }
405
406       // => highlight the candidate
407
setCandidates(keys);
408     }
409     else
410     {
411       // => highlight the candidates
412
setCandidates(keys);
413     }
414   }
415
416
417   private void highlightKeysStartingWith(String JavaDoc sub)
418   {
419      Vector<KeyboardKey> nkeys = map.getKeyStartingWithShortcut(sub);
420      if(nkeys.size()>0)
421      {
422         setCandidates(nkeys);
423      }
424   }
425
426
427   private void resetHighlights()
428   {
429      if(map!=null)
430      {
431           for(KeyboardKey key: map.getKeys())
432           {
433             key.setIsCandidate(false);
434           }
435      }
436   }
437
438
439   private void setCandidates(Vector<KeyboardKey> keys)
440   {
441       StringBuffer JavaDoc completion = new StringBuffer JavaDoc();
442       for(KeyboardKey k: keys)
443       {
444         k.setIsCandidate(true);
445         completion.append(""+k+" = [");
446         for(String JavaDoc s: k.getAllShortcuts())
447         {
448           completion.append(""+s+" ");
449         }
450         completion.setLength(completion.length()-4); // remove last separator
451

452         completion.append("], ");
453       }
454       completion.setLength(completion.length()-6); // remove last separator
455
this.completionLabel.setText(completion.toString());
456   }
457
458
459   private void addButton(final KeyboardKey key)
460   {
461     nButtons++;
462
463     JComponent toAdd = null;
464     if(key.isSpacer())
465     {
466       toAdd = new JLabel("");
467     }
468     else
469     {
470       JButton button = new JButton(""+key.toStringCAPITAL());
471       key.button = button;
472       toAdd = button;
473       button.addActionListener(new ActionListener()
474       {
475         public void actionPerformed(ActionEvent ae)
476         {
477            if((ae.getModifiers() & ae.SHIFT_MASK) == ae.SHIFT_MASK)
478            {
479              for(KeyboardListener kl: listeners)
480              {
481                kl.typed(""+key.toStringCAPITAL());
482              }
483            }
484            else
485            {
486              for(KeyboardListener kl: listeners)
487              {
488                kl.typed(""+key.toStringSMALL());
489              }
490            }
491         }
492       });
493
494       button.addMouseListener(new MouseAdapter()
495       {
496          @Override JavaDoc public void mouseEntered(MouseEvent me)
497          {
498             setTitle(title+" "+key+" = "+key.toStringAllShortcuts());
499
500          }
501
502          @Override JavaDoc public void mouseExited(MouseEvent me)
503          {
504             setTitle(title);
505          }
506       });
507
508     }
509
510     constr.fill = constr.BOTH;
511
512     actualCol++;
513     if(actualCol==numberOfCols)
514     {
515       constr.gridwidth = GridBagConstraints.REMAINDER;
516       actualCol=0;
517     }
518     else
519     {
520       constr.gridwidth = 1;
521     }
522
523     if(nButtons<=map.getVisibleKeys())
524     {
525       gridBag.setConstraints(toAdd, constr);
526       keyboardPanel.add(toAdd);
527     }
528
529
530
531   }
532
533   public static void main(String JavaDoc[] a)
534   {
535     JFrame f = new JFrame();
536     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
537
538     JTextPane pane = new JTextPane();
539     f.setContentPane(new JScrollPane(pane));
540     pane.setFont(new Font("LucidaSansRegular", Font.PLAIN, 18));
541
542     f.setDefaultCloseOperation(f.EXIT_ON_CLOSE);
543     f.setSize (300, 100);
544     f.setVisible(true);
545
546     KeyboardDialog kd = new KeyboardDialog(f);
547     kd.setKeyboard(new PolishMap());
548     kd.setTarget(pane);
549     kd.pack();
550     kd.setLocation (200, 200);
551     kd.setVisible(true);
552   }
553
554 } // KeyboardDialog
Popular Tags