Source for javax.swing.JTextField

   1: /* JTextField.java --
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Dimension;
  42: import java.awt.Font;
  43: import java.awt.FontMetrics;
  44: import java.awt.event.ActionEvent;
  45: import java.awt.event.ActionListener;
  46: import java.beans.PropertyChangeEvent;
  47: import java.beans.PropertyChangeListener;
  48: 
  49: import javax.accessibility.AccessibleContext;
  50: import javax.accessibility.AccessibleStateSet;
  51: import javax.swing.text.Document;
  52: import javax.swing.text.JTextComponent;
  53: import javax.swing.text.PlainDocument;
  54: import javax.swing.text.TextAction;
  55: 
  56: public class JTextField extends JTextComponent
  57:   implements SwingConstants
  58: {
  59:   /**
  60:    * AccessibleJTextField
  61:    */
  62:   protected class AccessibleJTextField extends AccessibleJTextComponent
  63:   {
  64:     private static final long serialVersionUID = 8255147276740453036L;
  65: 
  66:     /**
  67:      * Constructor AccessibleJTextField
  68:      */
  69:     protected AccessibleJTextField()
  70:     {
  71:       super();
  72:     }
  73: 
  74:     /**
  75:      * Returns the accessible state of this <code>AccessibleJTextField</code>.
  76:      *
  77:      * @return the accessible state of this <code>AccessibleJTextField</code>
  78:      */
  79:     public AccessibleStateSet getAccessibleStateSet()
  80:     {
  81:       AccessibleStateSet state = super.getAccessibleStateSet();
  82:       // TODO: Figure out what state must be added here to the super's state.
  83:       return state;
  84:     }
  85:   }
  86: 
  87:   private static final long serialVersionUID = 353853209832607592L;
  88: 
  89:   private static final Action[] actions;
  90: 
  91:   /**
  92:    * Name of the action that gets sent when the content of the text field
  93:    * gets accepted.
  94:    */
  95:   public static final String notifyAction = "notify-field-accept";
  96:   
  97:   static
  98:     {
  99:       actions = new Action[1];
 100:       actions[0] = new TextAction(notifyAction)
 101:       {
 102:         public void actionPerformed(ActionEvent event)
 103:         {
 104:           JTextField textField = (JTextField) event.getSource();
 105:           textField.fireActionPerformed();
 106:         }
 107:       };
 108:     }
 109:   
 110:   private int columns;
 111:   private int align;
 112:   private int scrollOffset;
 113: 
 114:   /** @since 1.3 */
 115:   private Action action;
 116: 
 117:   /** @since 1.3 */
 118:   private String actionCommand;
 119:   
 120:   private PropertyChangeListener actionPropertyChangeListener;
 121: 
 122:   /**
 123:    * The horizontal visibility of the textfield.
 124:    */
 125:   private BoundedRangeModel horizontalVisibility;
 126: 
 127:   /**
 128:    * Creates a new instance of <code>JTextField</code>.
 129:    */
 130:   public JTextField()
 131:   {
 132:     this(null, null, 0);
 133:   }
 134: 
 135:   /**
 136:    * Creates a new instance of <code>JTextField</code>.
 137:    *
 138:    * @param text the initial text
 139:    */
 140:   public JTextField(String text)
 141:   {
 142:     this(null, text, 0);
 143:   }
 144:   
 145:   /**
 146:    * Creates a new instance of <code>JTextField</code>.
 147:    *
 148:    * @param columns the number of columns
 149:    *
 150:    * @exception IllegalArgumentException if columns %lt; 0
 151:    */
 152:   public JTextField(int columns)
 153:   {
 154:     this(null, null, columns);
 155:   }
 156: 
 157:   /**
 158:    * Creates a new instance of <code>JTextField</code>.
 159:    *
 160:    * @param text the initial text
 161:    * @param columns the number of columns
 162:    *
 163:    * @exception IllegalArgumentException if columns %lt; 0
 164:    */
 165:   public JTextField(String text, int columns)
 166:   {
 167:     this(null, text, columns);
 168:   }
 169: 
 170:   /**
 171:    * Creates a new instance of <code>JTextField</code>.
 172:    *
 173:    * @param doc the document to use
 174:    * @param text the initial text
 175:    * @param columns the number of columns
 176:    *
 177:    * @exception IllegalArgumentException if columns %lt; 0
 178:    */
 179:   public JTextField(Document doc, String text, int columns)
 180:   {
 181:     if (columns < 0)
 182:       throw new IllegalArgumentException();
 183: 
 184:     this.columns = columns;
 185: 
 186:     setDocument(doc == null ? createDefaultModel() : doc);
 187: 
 188:     if (text != null)
 189:       setText(text);
 190: 
 191:     // default value for alignment
 192:     align = LEADING;
 193: 
 194:     // Initialize the horizontal visibility model.
 195:     horizontalVisibility = new DefaultBoundedRangeModel();
 196:   }
 197: 
 198:   /**
 199:    * Creates the default model for this text field.
 200:    * This implementation returns an instance of <code>PlainDocument</code>.
 201:    *
 202:    * @return a new instance of the default model
 203:    */
 204:   protected Document createDefaultModel()
 205:   {
 206:     return new PlainDocument();
 207:   }
 208: 
 209:   /**
 210:    * Returns the class ID for the UI.
 211:    *
 212:    * @return "TextFieldUI";
 213:    */
 214:   public String getUIClassID()
 215:   {
 216:     return "TextFieldUI";
 217:   }
 218: 
 219:   /**
 220:    * Adds a new listener object to this text field.
 221:    *
 222:    * @param listener the listener to add
 223:    */
 224:   public void addActionListener(ActionListener listener)
 225:   {
 226:     listenerList.add(ActionListener.class, listener);
 227:   }
 228: 
 229:   /**
 230:    * Removes a listener object from this text field.
 231:    *
 232:    * @param listener the listener to remove
 233:    */
 234:   public void removeActionListener(ActionListener listener)
 235:   {
 236:     listenerList.remove(ActionListener.class, listener);
 237:   }
 238: 
 239:   /**
 240:    * Returns all registered <code>ActionListener</code> objects.
 241:    *
 242:    * @return an array of listeners
 243:    *
 244:    * @since 1.4
 245:    */
 246:   public ActionListener[] getActionListeners()
 247:   {
 248:     return (ActionListener[]) getListeners(ActionListener.class);
 249:   }
 250: 
 251:   /**
 252:    * Sends an action event to all registered
 253:    * <code>ActionListener</code> objects.
 254:    */
 255:   protected void fireActionPerformed()
 256:   {
 257:     ActionEvent event = new ActionEvent(this, 0, notifyAction);
 258:     ActionListener[] listeners = getActionListeners();
 259: 
 260:     for (int index = 0; index < listeners.length; ++index)
 261:       listeners[index].actionPerformed(event);
 262:   }
 263: 
 264:   /**
 265:    * Returns the number of columns of this text field.
 266:    *
 267:    * @return the number of columns
 268:    */
 269:   public int getColumns()
 270:   {
 271:     return columns;
 272:   }
 273: 
 274:   /**
 275:    * Sets the number of columns and then invalidates the layout.
 276:    * @param columns the number of columns
 277:    * @throws IllegalArgumentException if columns < 0
 278:    */
 279:   public void setColumns(int columns)
 280:   {
 281:     if (columns < 0)
 282:       throw new IllegalArgumentException();
 283: 
 284:     this.columns = columns;
 285:     invalidate();
 286:     //FIXME: do we need this repaint call?
 287:     repaint();
 288:   }
 289: 
 290:   /**
 291:    * Returns the horizontal alignment, which is one of: JTextField.LEFT, 
 292:    * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING, 
 293:    * JTextField.TRAILING.
 294:    * @return the horizontal alignment
 295:    */
 296:   public int getHorizontalAlignment()
 297:   {
 298:     return align;
 299:   }
 300: 
 301:   /**
 302:    * Sets the horizontal alignment of the text.  Calls invalidate and repaint
 303:    * and fires a property change event.
 304:    * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER,
 305:    * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING.
 306:    * @throws IllegalArgumentException if newAlign is not one of the above.
 307:    */
 308:   public void setHorizontalAlignment(int newAlign)
 309:   {
 310:     //FIXME: should throw an IllegalArgumentException if newAlign is invalid
 311:     if (align == newAlign)
 312:       return;
 313: 
 314:     int oldAlign = align;
 315:     align = newAlign;
 316:     firePropertyChange("horizontalAlignment", oldAlign, newAlign);
 317:     invalidate();
 318:     repaint();
 319:   }
 320: 
 321:   /**
 322:    * Sets the current font and revalidates so the font will take effect.
 323:    */
 324:   public void setFont(Font newFont)
 325:   {
 326:     super.setFont(newFont);
 327:     revalidate();
 328:   }
 329: 
 330:   /**
 331:    * Returns the preferred size.  If there is a non-zero number of columns, 
 332:    * this is the number of columns multiplied by the column width, otherwise
 333:    * it returns super.getPreferredSize().
 334:    */
 335:   public Dimension getPreferredSize()
 336:   {
 337:     Dimension size = super.getPreferredSize();
 338: 
 339:     if (columns != 0)
 340:       size.width = columns * getColumnWidth();
 341: 
 342:     return size;
 343:   }
 344: 
 345:   /**
 346:    * Returns the scroll offset in pixels.
 347:    *
 348:    * @return the scroll offset
 349:    */
 350:   public int getScrollOffset()
 351:   {
 352:     //FIXME: this should return horizontalVisibility's value
 353:     return scrollOffset;
 354:   }
 355: 
 356:   /**
 357:    * Sets the scroll offset in pixels.
 358:    * 
 359:    * @param offset the scroll offset
 360:    */
 361:   public void setScrollOffset(int offset)
 362:   {
 363:     //FIXME: this should actualy scroll the field if needed
 364:     scrollOffset = offset;
 365:   }
 366: 
 367:   /**
 368:    * Returns the set of Actions that are commands for the editor.
 369:    * This is the actions supported by this editor plus the actions
 370:    * of the UI (returned by JTextComponent.getActions()).
 371:    */
 372:   public Action[] getActions()
 373:   {
 374:     return TextAction.augmentList(super.getActions(), actions);
 375:   }
 376: 
 377:   public void postActionEvent()
 378:   {
 379:     String command = actionCommand != null ? actionCommand : getText();
 380:     ActionEvent event = new ActionEvent(this, 0, command);
 381:     ActionListener[] listeners = getActionListeners();
 382: 
 383:     for (int index = 0; index < listeners.length; ++index)
 384:       listeners[index].actionPerformed(event);
 385:   }
 386:   
 387:   /**
 388:    * @since 1.3
 389:    */
 390:   public Action getAction()
 391:   {
 392:     return action;
 393:   }
 394: 
 395:   /**
 396:    * @since 1.3
 397:    */
 398:   public void setAction(Action newAction)
 399:   {
 400:     if (action == newAction)
 401:       return;
 402: 
 403:     if (action != null)
 404:       {
 405:         removeActionListener(action);
 406:         action.removePropertyChangeListener(actionPropertyChangeListener);
 407:         actionPropertyChangeListener = null;
 408:       }
 409: 
 410:     Action oldAction = action;
 411:     action = newAction;
 412: 
 413:     if (action != null)
 414:       {
 415:         addActionListener(action);
 416:         actionPropertyChangeListener = createActionPropertyChangeListener(action);
 417:         action.addPropertyChangeListener(actionPropertyChangeListener);
 418:       }
 419: 
 420:     //FIXME: is this a hack?  The horizontal alignment hasn't changed
 421:     firePropertyChange("horizontalAlignment", oldAction, newAction);
 422:   }
 423: 
 424:   /**
 425:    * Sets the command string used in action events.
 426:    * @since 1.3
 427:    */
 428:   public void setActionCommand(String command)
 429:   {
 430:     actionCommand = command;
 431:   }
 432: 
 433:   /**
 434:    * @since 1.3
 435:    */
 436:   protected PropertyChangeListener createActionPropertyChangeListener(Action action)
 437:   {
 438:     return new PropertyChangeListener()
 439:     {
 440:       public void propertyChange(PropertyChangeEvent event)
 441:       {
 442:         // Update properties "action" and "horizontalAlignment".
 443:         String name = event.getPropertyName();
 444: 
 445:         if (name.equals("enabled"))
 446:           {
 447:             boolean enabled = ((Boolean) event.getNewValue()).booleanValue();
 448:             JTextField.this.setEnabled(enabled);
 449:           }
 450:         else if (name.equals(Action.SHORT_DESCRIPTION))
 451:           {
 452:             JTextField.this.setToolTipText((String) event.getNewValue());
 453:           }
 454:       }
 455:     };
 456:   }
 457: 
 458:   /**
 459:    * 
 460:    * @since 1.3
 461:    */
 462:   protected void configurePropertiesFromAction(Action action)
 463:   {
 464:     if (action != null)
 465:       {
 466:         setEnabled(action.isEnabled());
 467:         setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION));
 468:       }
 469:     else
 470:       {
 471:         setEnabled(true);
 472:         setToolTipText(null);
 473:       }
 474:   }
 475: 
 476:   /**
 477:    * Returns the column width, which is the width of the character m
 478:    * for the font in use.
 479:    * @return the width of the character m for the font in use.
 480:    */
 481:   protected int getColumnWidth()
 482:   {
 483:     FontMetrics metrics = getToolkit().getFontMetrics(getFont());
 484:     return metrics.charWidth('m');
 485:   }
 486: 
 487:   /**
 488:    * Returns the accessible context associated with the <code>JTextField</code>.
 489:    *
 490:    * @return the accessible context associated with the <code>JTextField</code>
 491:    */
 492:   public AccessibleContext getAccessibleContext()
 493:   {
 494:     if (accessibleContext == null)
 495:       accessibleContext = new AccessibleJTextField();
 496:     return accessibleContext;
 497:   }
 498: 
 499:   /**
 500:    * Returns the bounded range model that describes the horizontal visibility
 501:    * of the text field in the case when the text does not fit into the
 502:    * available space. The actual values of this model are managed by the look
 503:    * and feel implementation.
 504:    *
 505:    * @return the bounded range model that describes the horizontal visibility
 506:    */
 507:   public BoundedRangeModel getHorizontalVisibility()
 508:   {
 509:     // TODO: The real implementation of this property is still missing.
 510:     // However, this is not done in JTextField but must instead be handled in
 511:     // javax.swing.text.FieldView.
 512:     return horizontalVisibility;
 513:   }
 514: }