Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 251   Methods: 27
NCLOC: 167   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
TextField.java 70.8% 85.5% 74.1% 80%
coverage coverage
 1   
 package abbot.editor.widgets;
 2   
 
 3   
 import java.awt.*;
 4   
 import java.awt.event.*;
 5   
 
 6   
 import javax.swing.*;
 7   
 import javax.swing.event.*;
 8   
 
 9   
 import abbot.Log;
 10   
 
 11   
 /** A better text field with some useful features.
 12   
     <ul>
 13   
     <li>Fires when focus leaves the component.
 14   
     <li>Selects all the contents when the action is fired to indicate the
 15   
     contents were accepted.
 16   
     <li>Until the user causes a notify-field-accept (usually with the 
 17   
     Enter key), the contents may be reverted to the original value by invoking 
 18   
     the field-revert action (bound to ESC by default).
 19   
     <li>Actions are fired on all edits.
 20   
     <li>The field has a fixed height.
 21   
     <li>Pressing Enter when the field is blank will insert a default value, if
 22   
     one has been provided.
 23   
     </ul>
 24   
     <p>
 25   
     This functionality may be applied to any JTextField with the
 26   
     {@link #decorate(JTextField)} method. 
 27   
 */
 28   
 public class TextField extends JTextField {
 29   
 
 30   
     /** Action command when the field loses focus. */
 31   
     public static final String ACTION_FOCUS_LOST = "focus-lost";
 32   
     /** Action command when the text changes. */
 33   
     public static final String ACTION_TEXT_CHANGED = "text-changed";
 34   
     /** Action command when text is inserted. */
 35   
     public static final String ACTION_TEXT_INSERTED = "text-inserted";
 36   
     /** Action command when text is removed. */
 37   
     public static final String ACTION_TEXT_REMOVED = "text-removed";
 38   
     /** Action command when the field reverts to its original value.  The
 39   
      * action is equivalent to typing the original text and hitting "enter".
 40   
      */
 41   
     public static final String ACTION_TEXT_REVERTED = "text-reverted";
 42   
 
 43   
     private static final String REVERT_ACTION_NAME = "field-revert";
 44   
 
 45  294
     public static boolean isDocumentAction(String action) {
 46  294
         return action == ACTION_TEXT_CHANGED
 47   
             || action == ACTION_TEXT_INSERTED
 48   
             || action == ACTION_TEXT_REMOVED;
 49   
     }
 50   
 
 51  24
     public static void decorate(JTextField tf) {
 52  24
         new Decorator(tf);
 53   
     }
 54   
 
 55  0
     public static void decorate(JTextField tf, String defaultValue) {
 56  0
         new Decorator(tf, defaultValue);
 57   
     }
 58   
 
 59   
     /** Avoid recursive changes to the field's text. */
 60   
     private boolean notifying;
 61   
     private Decorator decorator;
 62   
 
 63  59
     public TextField(String value, int columns) {
 64  59
         super(value, columns);
 65  59
         decorator = new Decorator(this);
 66   
     }
 67   
 
 68  0
     public TextField(String value, String defaultValue, int columns) {
 69  0
         super(value, columns);
 70  0
         decorator = new Decorator(this, defaultValue);
 71   
     }
 72   
 
 73  7
     public TextField(String value) {
 74  7
         super(value);
 75  7
         decorator = new Decorator(this);
 76   
     }
 77   
 
 78  64
     public TextField(String value, String defaultValue) {
 79  64
         super(value);
 80  64
         decorator = new Decorator(this, defaultValue);
 81   
     }
 82   
 
 83   
     /** Don't allow text field to resize height. */
 84  126
     public Dimension getMaximumSize() {
 85  126
         Dimension size = super.getMaximumSize();
 86  126
         size.height = super.getPreferredSize().height;
 87  126
         return size;
 88   
     }
 89   
 
 90   
     /** Don't allow text field to resize height. */
 91  207
     public Dimension getMinimumSize() {
 92  207
         Dimension size = super.getMinimumSize();
 93  207
         size.height = super.getPreferredSize().height;
 94  207
         return size;
 95   
     }
 96   
 
 97   
     /** The default value will be inserted when the field is blank and ENTER
 98   
         is pressed.  This behavior is disabled if the value is null.
 99   
     */
 100  0
     public void setDefaultValue(String value) {
 101  0
         decorator.setDefaultValue(value);
 102   
     }
 103   
 
 104  176
     public void setText(String text) {
 105  176
         if (!getText().equals(text) && !notifying)
 106  139
             super.setText(text != null ? text : "");
 107   
     }
 108   
 
 109  204
     protected void fireActionPerformed() {
 110  204
         notifying = true;
 111  204
         try {
 112  204
             super.fireActionPerformed();
 113   
         }
 114   
         finally {
 115  204
             notifying = false;
 116   
         }
 117   
     }
 118   
 
 119   
     public static class Decorator {
 120   
         private JTextField textField;
 121   
         /** Text used when field is reverted.  Updated on any
 122   
             notify-field-accept action or when setText() is invoked directly.
 123   
         */
 124   
         private String revertText;
 125   
         // whether to notify action listeners on every text change
 126   
         private boolean continuousFire = true;
 127   
         // Value to place in field when it is empty and Enter is hit
 128   
         private String defaultValue;
 129   
 
 130  90
         public Decorator(JTextField textField) {
 131  90
             this(textField, null);
 132   
         }
 133   
 
 134  154
         public Decorator(final JTextField textField, String defValue) {
 135  154
             this.textField = textField;
 136  154
             this.defaultValue = defValue;
 137  154
             textField.addFocusListener(new java.awt.event.FocusAdapter() {
 138  29
                 public void focusLost(java.awt.event.FocusEvent ev) {
 139  29
                     if (!ev.isTemporary() && !isLocalMenuActive(textField)) {
 140  13
                         fireActionPerformed(ACTION_FOCUS_LOST);
 141   
                     }
 142   
                 }
 143   
             });
 144  154
             DocumentListener listener = new DocumentListener() {
 145  0
                 public void changedUpdate(DocumentEvent ev) {
 146  0
                     if (continuousFire) {
 147  0
                         fireActionPerformed(ACTION_TEXT_CHANGED);
 148   
                     }
 149   
                 }
 150  174
                 public void insertUpdate(DocumentEvent ev) {
 151   
                     // If setText is called, update the revert text
 152  174
                     String stack = Log.getStack(Log.FULL_STACK);
 153  174
                     if (stack.indexOf("JTextComponent.setText") != -1) {
 154  66
                         revertText = textField.getText();
 155   
                     }
 156  174
                     if (continuousFire) {
 157  174
                         fireActionPerformed(ACTION_TEXT_INSERTED);
 158   
                     }
 159   
                 }
 160  73
                 public void removeUpdate(DocumentEvent ev) {
 161  73
                     if (continuousFire) {
 162  73
                         fireActionPerformed(ACTION_TEXT_REMOVED);
 163   
                     }
 164   
                 }
 165   
             };
 166  154
             textField.getDocument().addDocumentListener(listener);
 167  154
             textField.addActionListener(new ActionListener() {
 168  267
                 public void actionPerformed(ActionEvent e) {
 169   
                     // Select all text when there is an effective commit,
 170   
                     // and make note of the new committed text.  
 171   
                     // If the field is blank, set the default value if there
 172   
                     // is one. 
 173  267
                     String text = textField.getText();
 174  267
                     if (!isDocumentAction(e.getActionCommand())) {
 175  20
                         if (defaultValue != null && "".equals(text)) {
 176  1
                             text = defaultValue;
 177  1
                             SwingUtilities.invokeLater(new Runnable() {
 178  1
                                 public void run() {
 179  1
                                     textField.setText(defaultValue);
 180  1
                                     textField.selectAll();
 181   
                                 }
 182   
                             });
 183   
                         }
 184  20
                         revertText = text;
 185  20
                         textField.selectAll();
 186   
                     }
 187   
                 }
 188   
             });
 189   
 
 190   
             // Changing the input map doesn't work on the JComboBox editor,
 191   
             // so use a key listener instead.
 192  154
             textField.addKeyListener(new KeyAdapter() {
 193  138
                 public void keyPressed(KeyEvent e) {
 194  138
                     if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
 195  2
                         revertText();
 196   
                     }
 197   
                 }
 198   
             });
 199   
 
 200   
             /*
 201   
             // This would appear to be a better method for handling revert,
 202   
             // but the following code doesn't work, and I can't figure out why
 203   
             ActionMap am = textField.getActionMap();
 204   
             am.put(REVERT_ACTION_NAME, new RevertFieldAction());
 205   
             
 206   
             InputMap im = textField.getInputMap();
 207   
             im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
 208   
                    REVERT_ACTION_NAME);
 209   
             */
 210   
             // Initialize
 211  154
             revertText = textField.getText();
 212   
         }
 213   
 
 214  0
         private void setDefaultValue(String value) {
 215  0
             this.defaultValue = value;
 216   
         }
 217   
 
 218  2
         private void revertText() {
 219  2
             if (!textField.getText().equals(revertText)) {
 220  2
                 textField.setText(revertText);
 221  2
                 fireActionPerformed(ACTION_TEXT_REVERTED);
 222   
             }
 223   
         }
 224   
 
 225  262
         private void fireActionPerformed(String command) {
 226  262
             textField.setActionCommand(command);
 227  262
             textField.postActionEvent();
 228  262
             textField.setActionCommand(null);
 229   
         }
 230   
 
 231   
         /** Detect temporary focus loss due to menu activation (pre-1.4). */
 232  13
         private boolean isLocalMenuActive(JTextField field) {
 233  13
             Window window = SwingUtilities.getWindowAncestor(field);
 234  13
             if (window != null) {
 235  13
                 Component comp = window.getFocusOwner();
 236  13
                 return comp != null && (comp instanceof JMenuItem);
 237   
             }
 238  0
             return false;
 239   
         }
 240   
 
 241   
         protected class RevertFieldAction extends AbstractAction {
 242  0
             public RevertFieldAction() {
 243  0
                 super(REVERT_ACTION_NAME);
 244   
             }
 245  0
             public void actionPerformed(ActionEvent e) {
 246  0
                 revertText();
 247   
             }
 248   
         }
 249   
     }
 250   
 }
 251