SuperJTextField.java

00001 /*
00002  * August 19, 2005 -- Merged AutoCompleteTextField with SuperJTextField (ronyeh)
00003  * March 10, 2005 -- Created AutoCompleteTextField
00004  */
00005 package edu.stanford.hci.r3.util.components;
00006 
00007 import java.awt.BorderLayout;
00008 import java.awt.Color;
00009 import java.awt.Font;
00010 import java.awt.event.FocusEvent;
00011 import java.awt.event.FocusListener;
00012 import java.awt.event.KeyAdapter;
00013 import java.awt.event.KeyEvent;
00014 import java.text.NumberFormat;
00015 import java.text.ParseException;
00016 import java.util.LinkedList;
00017 import java.util.List;
00018 
00019 import javax.swing.JButton;
00020 import javax.swing.JFrame;
00021 import javax.swing.JTextField;
00022 import javax.swing.text.*;
00023 
00044 public class SuperJTextField extends JTextField {
00045 
00051         private class SuperDocument extends PlainDocument {
00052 
00053                 private SuperJTextField parent;
00054 
00058                 public SuperDocument(SuperJTextField p) {
00059                         parent = p;
00060                 }
00061 
00062                 /*
00063                  * (non-Javadoc)
00064                  * 
00065                  * @see javax.swing.text.Document#insertString(int, java.lang.String,
00066                  *      javax.swing.text.AttributeSet)
00067                  */
00068                 public void insertString(int offs, String str, AttributeSet attributeSet)
00069                                 throws BadLocationException {
00070                         super.insertString(offs, str, attributeSet);
00071                         // check the current text, to see if it matches any prefix
00072                         // and that the current text is not a parsable number
00073                         // if there is only one match, then enter the rest of that text into
00074                         // this document!
00075 
00076                         final String myText = parent.getText().toLowerCase();
00077                         String match = null;
00078 
00079                         // if the current text is a parsable number, then we're done!
00080                         // don't autocomplete numbers
00081                         try {
00082                                 // try to cause an exception here; if an exception, that means that it is NOT a
00083                                 // number
00084                                 // The exception will skip the return; line, and we will be able to continue
00085                                 NumberFormat.getInstance().parse(myText);
00086                                 // System.out.println("Number is: " + n);
00087                                 return;
00088                         } catch (ParseException e) {
00089                                 // do nothing
00090                                 // e.printStackTrace();
00091                         }
00092 
00093                         // look at all the strings that are possible completions
00094                         String restOfText = null;
00095 
00096                         // we should pull the first one (MRU), because we always populate head first....
00097                         for (final String testString : possibleCompletions) {
00098                                 // match! The toLowerCase() thing replicates Excel's behavior
00099                                 if (testString.toLowerCase().startsWith(myText)) {
00100 
00101                                         // instead of shortest (this was a bug in LosTuxtlas)
00102                                         // choose the most recent
00103                                         match = testString;
00104                                         restOfText = match.substring(myText.length());
00105 
00106                                         // a match, autocomplete!
00107                                         // Logger.log("AutoComplete::One Match Found: " + match);
00108                                         // Logger.log("The rest of the text is: " + restOfText);
00109                                         super.insertString(offs + 1, restOfText, attributeSet);
00110                                         final Caret c = parent.getCaret();
00111                                         c.setDot(match.length());
00112                                         c.moveDot(offs + 1);
00113                                         autocompleteJustHappened = true;
00114                                         break;
00115                                 }
00116                         }
00117                 }
00118         }
00119 
00123         public static void main(String[] args) {
00124                 final JFrame f = new JFrame();
00125                 SuperJTextField stf = new SuperJTextField("Hello");
00126                 {
00127                         stf.setFont(new Font("Verdana", Font.PLAIN, 11));
00128                         stf.setInputHintFont(new Font("Verdana", Font.ITALIC, 11));
00129                         stf.setInputHint("<Search>");
00130                 }
00131                 {
00132                         stf.addPossibleCompletion("Bumpkin");
00133                         stf.addPossibleCompletion("Pumpkin");
00134                         stf.addPossibleCompletion("Word Up");
00135                         stf.addPossibleCompletion("821.56");
00136                 }
00137 
00138                 f.add(stf, BorderLayout.CENTER);
00139                 f.add(new JButton("X"), BorderLayout.EAST);
00140                 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
00141                 f.pack();
00142                 f.setVisible(true);
00143         }
00144 
00145         private boolean autocompleteJustHappened = false;
00146 
00147         private boolean focusOnEdit;
00148 
00149         private Color hintColor = Color.LIGHT_GRAY;
00150 
00151         private String inputHint;
00152 
00153         private Font inputHintFont;
00154 
00155         protected boolean inputHintShowing;
00156 
00157         // last key user pressed
00158         protected int lastKeyCode = -1;
00159 
00160         private boolean lastKeyShiftDown;
00161 
00162         // for each completion, we track how many times it has been entered into this box
00163         // it should either match to the most popular, or the latest...
00164         // I think either one will work, but in Los Tuxtlas, basically the latest was good
00165         // because people corrected mistakes, and our UI sucked rocks.
00166         // Excel basically does the one match... If there are other matches, it does nothing (as opposed
00167         // to putting in wrong data; tradeoff between efficiency and errors)
00168         // the real solution here would be to drop down a text box with popular completions, like most
00169         // cool text boxes nowadays.
00170         // for now, we will just do the most recent.
00171         // protected HashMap<String, Integer> possibleCompletions = new HashMap<String, Integer>();
00172         private LinkedList<String> possibleCompletions = new LinkedList<String>();
00173 
00174         //
00175         protected Color textColor = Color.BLACK;
00176 
00177         //
00178         protected Font textFont;
00179 
00183         public SuperJTextField() {
00184                 setup();
00185         }
00186 
00190         public SuperJTextField(int numCols) {
00191                 super(numCols);
00192                 setup();
00193         }
00194 
00198         public SuperJTextField(String string) {
00199                 super();
00200                 setup();
00201                 setText(string);
00202         }
00203 
00208         public SuperJTextField(String defaultText, int numCols) {
00209                 super(numCols);
00210                 setup();
00211                 setText(defaultText);
00212         }
00213 
00222         public void addPossibleCompletion(String completion) {
00223                 possibleCompletions.addFirst(completion);
00224         }
00225 
00226         /*
00227          * (non-Javadoc)
00228          * 
00229          * @see javax.swing.JTextField#createDefaultModel()
00230          */
00231         protected Document createDefaultModel() {
00232                 return new SuperDocument(this);
00233         }
00234 
00241         public int getExitKey() {
00242                 return lastKeyCode;
00243         }
00244 
00248         public String getInputHint() {
00249                 return inputHint;
00250         }
00251 
00255         public boolean getLastKeyShiftDown() {
00256                 return lastKeyShiftDown;
00257         }
00258 
00259         /*
00260          * (non-Javadoc)
00261          * 
00262          * @see javax.swing.text.JTextComponent#getText()
00263          */
00264         public String getText() {
00265                 if (inputHintShowing) {
00266                         return "";
00267                 } else {
00268                         return super.getText();
00269                 }
00270         }
00271 
00275         protected boolean noText() {
00276                 return super.getText() == null || super.getText().equals("");
00277         }
00278 
00282         public void removePossibleCompletion(String completion) {
00283                 possibleCompletions.remove(completion);
00284         }
00285 
00286         public void setFocusGainedOnEdit(boolean foe) {
00287                 focusOnEdit = foe;
00288         }
00289 
00295         public void setInputHint(String hint) {
00296                 inputHint = hint;
00297                 if (noText()) {
00298                         setInputHintVisible(true);
00299                 }
00300         }
00301 
00305         private void setInputHintFont(Font font) {
00306                 inputHintFont = font;
00307         }
00308 
00312         private void setInputHintVisible(boolean toBeVisible) {
00313                 if (toBeVisible) { // show
00314                         if (inputHint != null && !inputHint.equals("")) {
00315                                 setForeground(hintColor);
00316                                 super.setText(inputHint);
00317                                 setFont(inputHintFont);
00318                                 inputHintShowing = true;
00319                         }
00320                 } else { // hide
00321                         if (inputHintShowing) {
00322                                 super.setText("");
00323                                 setForeground(textColor);
00324                                 setFont(textFont);
00325                                 inputHintShowing = false;
00326                         }
00327                 }
00328         }
00329 
00333         public void setPossibleCompletions(final List<String> possible) {
00334                 possibleCompletions.clear(); // clear the list
00335                 for (final String entry : possible) {
00336                         possibleCompletions.addFirst(entry);
00337                 }
00338         }
00339 
00340         /*
00341          * (non-Javadoc)
00342          * 
00343          * @see javax.swing.text.JTextComponent#setText(java.lang.String)
00344          */
00345         @Override
00346         public void setText(String t) {
00347                 super.setText(t);
00348 
00349                 if (noText()) {
00350                         setInputHintVisible(true);
00351                 } else {
00352                         setForeground(textColor);
00353                         inputHintShowing = false;
00354                 }
00355         }
00356 
00360         private void setup() {
00361                 textFont = getFont();
00362                 inputHintFont = getFont();
00363 
00364                 addFocusListener(new FocusListener() {
00365 
00366                         // if someone clicks on me
00367                         // if there was no text to begin with, clear the textfield
00368                         // if there was text to begin with, set it
00369                         public void focusGained(FocusEvent arg0) {
00370                                 setForeground(textColor);
00371                                 setFont(textFont);
00372 
00373                                 setInputHintVisible(false);
00374 
00375                                 final int length = SuperJTextField.super.getText().length();
00376 
00377                                 // if autocomplete did not just happen, we select everything
00378                                 // if autocomplete JUST happened, we do nothing
00379                                 // if we focused when we tried to edit a cell, we do NOT select everything
00380                                 if (!autocompleteJustHappened && !focusOnEdit) {
00381                                         select(0, length);
00382                                 }
00383                                 // for fixing the JTable bug
00384                                 autocompleteJustHappened = false;
00385                                 focusOnEdit = false;
00386                         }
00387 
00388                         // if someone clicks or tabs away
00389                         // if there was text to begin with, just gray it out
00390                         public void focusLost(FocusEvent arg0) {
00391                                 // System.out.println("Focus Lost");
00392 
00393                                 if (noText()) {
00394                                         setInputHintVisible(true);
00395                                 }
00396                                 autocompleteJustHappened = false;
00397                         }
00398                 });
00399 
00400                 addKeyListener(new KeyAdapter() {
00401 
00402                         public void keyPressed(KeyEvent ke) {
00403 
00404                                 // what was the last key pressed?
00405                                 lastKeyCode = ke.getKeyCode();
00406                                 // System.out.println(lastKeyCode);
00407 
00408                                 lastKeyShiftDown = ke.isShiftDown();
00409                         }
00410                 });
00411         }
00412 
00413 }

Generated on Sat Apr 14 18:21:39 2007 for R3 Paper Toolkit by  doxygen 1.4.7