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:     PlainDocument doc = new PlainDocument();
 207:     doc.putProperty("filterNewlines", Boolean.TRUE);
 208:     return doc;
 209:   }
 210: 
 211:   /**
 212:    * Returns the class ID for the UI.
 213:    *
 214:    * @return "TextFieldUI";
 215:    */
 216:   public String getUIClassID()
 217:   {
 218:     return "TextFieldUI";
 219:   }
 220: 
 221:   /**
 222:    * Adds a new listener object to this text field.
 223:    *
 224:    * @param listener the listener to add
 225:    */
 226:   public void addActionListener(ActionListener listener)
 227:   {
 228:     listenerList.add(ActionListener.class, listener);
 229:   }
 230: 
 231:   /**
 232:    * Removes a listener object from this text field.
 233:    *
 234:    * @param listener the listener to remove
 235:    */
 236:   public void removeActionListener(ActionListener listener)
 237:   {
 238:     listenerList.remove(ActionListener.class, listener);
 239:   }
 240: 
 241:   /**
 242:    * Returns all registered <code>ActionListener</code> objects.
 243:    *
 244:    * @return an array of listeners
 245:    *
 246:    * @since 1.4
 247:    */
 248:   public ActionListener[] getActionListeners()
 249:   {
 250:     return (ActionListener[]) getListeners(ActionListener.class);
 251:   }
 252: 
 253:   /**
 254:    * Sends an action event to all registered
 255:    * <code>ActionListener</code> objects.
 256:    */
 257:   protected void fireActionPerformed()
 258:   {
 259:     ActionEvent event = new ActionEvent(this, 0, notifyAction);
 260:     ActionListener[] listeners = getActionListeners();
 261: 
 262:     for (int index = 0; index < listeners.length; ++index)
 263:       listeners[index].actionPerformed(event);
 264:   }
 265: 
 266:   /**
 267:    * Returns the number of columns of this text field.
 268:    *
 269:    * @return the number of columns
 270:    */
 271:   public int getColumns()
 272:   {
 273:     return columns;
 274:   }
 275: 
 276:   /**
 277:    * Sets the number of columns and then invalidates the layout.
 278:    * @param columns the number of columns
 279:    * @throws IllegalArgumentException if columns < 0
 280:    */
 281:   public void setColumns(int columns)
 282:   {
 283:     if (columns < 0)
 284:       throw new IllegalArgumentException();
 285: 
 286:     this.columns = columns;
 287:     invalidate();
 288:     //FIXME: do we need this repaint call?
 289:     repaint();
 290:   }
 291: 
 292:   /**
 293:    * Returns the horizontal alignment, which is one of: JTextField.LEFT, 
 294:    * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING, 
 295:    * JTextField.TRAILING.
 296:    * @return the horizontal alignment
 297:    */
 298:   public int getHorizontalAlignment()
 299:   {
 300:     return align;
 301:   }
 302: 
 303:   /**
 304:    * Sets the horizontal alignment of the text.  Calls invalidate and repaint
 305:    * and fires a property change event.
 306:    * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER,
 307:    * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING.
 308:    * @throws IllegalArgumentException if newAlign is not one of the above.
 309:    */
 310:   public void setHorizontalAlignment(int newAlign)
 311:   {
 312:     //FIXME: should throw an IllegalArgumentException if newAlign is invalid
 313:     if (align == newAlign)
 314:       return;
 315: 
 316:     int oldAlign = align;
 317:     align = newAlign;
 318:     firePropertyChange("horizontalAlignment", oldAlign, newAlign);
 319:     invalidate();
 320:     repaint();
 321:   }
 322: 
 323:   /**
 324:    * Sets the current font and revalidates so the font will take effect.
 325:    */
 326:   public void setFont(Font newFont)
 327:   {
 328:     super.setFont(newFont);
 329:     revalidate();
 330:   }
 331: 
 332:   /**
 333:    * Returns the preferred size.  If there is a non-zero number of columns, 
 334:    * this is the number of columns multiplied by the column width, otherwise
 335:    * it returns super.getPreferredSize().
 336:    */
 337:   public Dimension getPreferredSize()
 338:   {
 339:     Dimension size = super.getPreferredSize();
 340: 
 341:     if (columns != 0)
 342:       size.width = columns * getColumnWidth();
 343: 
 344:     return size;
 345:   }
 346: 
 347:   /**
 348:    * Returns the scroll offset in pixels.
 349:    *
 350:    * @return the scroll offset
 351:    */
 352:   public int getScrollOffset()
 353:   {
 354:     //FIXME: this should return horizontalVisibility's value
 355:     return scrollOffset;
 356:   }
 357: 
 358:   /**
 359:    * Sets the scroll offset in pixels.
 360:    * 
 361:    * @param offset the scroll offset
 362:    */
 363:   public void setScrollOffset(int offset)
 364:   {
 365:     //FIXME: this should actualy scroll the field if needed
 366:     scrollOffset = offset;
 367:   }
 368: 
 369:   /**
 370:    * Returns the set of Actions that are commands for the editor.
 371:    * This is the actions supported by this editor plus the actions
 372:    * of the UI (returned by JTextComponent.getActions()).
 373:    */
 374:   public Action[] getActions()
 375:   {
 376:     return TextAction.augmentList(super.getActions(), actions);
 377:   }
 378: 
 379:   public void postActionEvent()
 380:   {
 381:     String command = actionCommand != null ? actionCommand : getText();
 382:     ActionEvent event = new ActionEvent(this, 0, command);
 383:     ActionListener[] listeners = getActionListeners();
 384: 
 385:     for (int index = 0; index < listeners.length; ++index)
 386:       listeners[index].actionPerformed(event);
 387:   }
 388:   
 389:   /**
 390:    * @since 1.3
 391:    */
 392:   public Action getAction()
 393:   {
 394:     return action;
 395:   }
 396: 
 397:   /**
 398:    * @since 1.3
 399:    */
 400:   public void setAction(Action newAction)
 401:   {
 402:     if (action == newAction)
 403:       return;
 404: 
 405:     if (action != null)
 406:       {
 407:         removeActionListener(action);
 408:         action.removePropertyChangeListener(actionPropertyChangeListener);
 409:         actionPropertyChangeListener = null;
 410:       }
 411: 
 412:     Action oldAction = action;
 413:     action = newAction;
 414: 
 415:     if (action != null)
 416:       {
 417:         addActionListener(action);
 418:         actionPropertyChangeListener = createActionPropertyChangeListener(action);
 419:         action.addPropertyChangeListener(actionPropertyChangeListener);
 420:       }
 421: 
 422:     //FIXME: is this a hack?  The horizontal alignment hasn't changed
 423:     firePropertyChange("horizontalAlignment", oldAction, newAction);
 424:   }
 425: 
 426:   /**
 427:    * Sets the command string used in action events.
 428:    * @since 1.3
 429:    */
 430:   public void setActionCommand(String command)
 431:   {
 432:     actionCommand = command;
 433:   }
 434: 
 435:   /**
 436:    * @since 1.3
 437:    */
 438:   protected PropertyChangeListener createActionPropertyChangeListener(Action action)
 439:   {
 440:     return new PropertyChangeListener()
 441:     {
 442:       public void propertyChange(PropertyChangeEvent event)
 443:       {
 444:         // Update properties "action" and "horizontalAlignment".
 445:         String name = event.getPropertyName();
 446: 
 447:         if (name.equals("enabled"))
 448:           {
 449:             boolean enabled = ((Boolean) event.getNewValue()).booleanValue();
 450:             JTextField.this.setEnabled(enabled);
 451:           }
 452:         else if (name.equals(Action.SHORT_DESCRIPTION))
 453:           {
 454:             JTextField.this.setToolTipText((String) event.getNewValue());
 455:           }
 456:       }
 457:     };
 458:   }
 459: 
 460:   /**
 461:    * 
 462:    * @since 1.3
 463:    */
 464:   protected void configurePropertiesFromAction(Action action)
 465:   {
 466:     if (action != null)
 467:       {
 468:         setEnabled(action.isEnabled());
 469:         setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION));
 470:       }
 471:     else
 472:       {
 473:         setEnabled(true);
 474:         setToolTipText(null);
 475:       }
 476:   }
 477: 
 478:   /**
 479:    * Returns the column width, which is the width of the character m
 480:    * for the font in use.
 481:    * @return the width of the character m for the font in use.
 482:    */
 483:   protected int getColumnWidth()
 484:   {
 485:     FontMetrics metrics = getToolkit().getFontMetrics(getFont());
 486:     return metrics.charWidth('m');
 487:   }
 488: 
 489:   /**
 490:    * Returns the accessible context associated with the <code>JTextField</code>.
 491:    *
 492:    * @return the accessible context associated with the <code>JTextField</code>
 493:    */
 494:   public AccessibleContext getAccessibleContext()
 495:   {
 496:     if (accessibleContext == null)
 497:       accessibleContext = new AccessibleJTextField();
 498:     return accessibleContext;
 499:   }
 500: 
 501:   /**
 502:    * Returns the bounded range model that describes the horizontal visibility
 503:    * of the text field in the case when the text does not fit into the
 504:    * available space. The actual values of this model are managed by the look
 505:    * and feel implementation.
 506:    *
 507:    * @return the bounded range model that describes the horizontal visibility
 508:    */
 509:   public BoundedRangeModel getHorizontalVisibility()
 510:   {
 511:     // TODO: The real implementation of this property is still missing.
 512:     // However, this is not done in JTextField but must instead be handled in
 513:     // javax.swing.text.FieldView.
 514:     return horizontalVisibility;
 515:   }
 516: }