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