Frames | No Frames |
1: /* JTextField.java -- 2: Copyright (C) 2002, 2004, 2005, 2006 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.Insets; 45: import java.awt.Rectangle; 46: import java.awt.event.ActionEvent; 47: import java.awt.event.ActionListener; 48: import java.beans.PropertyChangeEvent; 49: import java.beans.PropertyChangeListener; 50: 51: import javax.accessibility.AccessibleContext; 52: import javax.accessibility.AccessibleStateSet; 53: import javax.swing.text.Document; 54: import javax.swing.text.JTextComponent; 55: import javax.swing.text.PlainDocument; 56: import javax.swing.text.TextAction; 57: 58: public class JTextField extends JTextComponent 59: implements SwingConstants 60: { 61: /** 62: * AccessibleJTextField 63: */ 64: protected class AccessibleJTextField extends AccessibleJTextComponent 65: { 66: private static final long serialVersionUID = 8255147276740453036L; 67: 68: /** 69: * Constructor AccessibleJTextField 70: */ 71: protected AccessibleJTextField() 72: { 73: super(); 74: } 75: 76: /** 77: * Returns the accessible state of this <code>AccessibleJTextField</code>. 78: * 79: * @return the accessible state of this <code>AccessibleJTextField</code> 80: */ 81: public AccessibleStateSet getAccessibleStateSet() 82: { 83: AccessibleStateSet state = super.getAccessibleStateSet(); 84: // TODO: Figure out what state must be added here to the super's state. 85: return state; 86: } 87: } 88: 89: private static final long serialVersionUID = 353853209832607592L; 90: 91: private static final Action[] actions; 92: 93: /** 94: * Name of the action that gets sent when the content of the text field 95: * gets accepted. 96: */ 97: public static final String notifyAction = "notify-field-accept"; 98: 99: static 100: { 101: actions = new Action[1]; 102: actions[0] = new TextAction(notifyAction) 103: { 104: public void actionPerformed(ActionEvent event) 105: { 106: JTextField textField = (JTextField) event.getSource(); 107: textField.fireActionPerformed(); 108: } 109: }; 110: } 111: 112: private int columns; 113: private int align; 114: 115: /** @since 1.3 */ 116: private Action action; 117: 118: /** @since 1.3 */ 119: private String actionCommand; 120: 121: private PropertyChangeListener actionPropertyChangeListener; 122: 123: /** 124: * The horizontal visibility of the textfield. 125: */ 126: private BoundedRangeModel horizontalVisibility; 127: 128: /** 129: * Creates a new instance of <code>JTextField</code>. 130: */ 131: public JTextField() 132: { 133: this(null, null, 0); 134: } 135: 136: /** 137: * Creates a new instance of <code>JTextField</code>. 138: * 139: * @param text the initial text 140: */ 141: public JTextField(String text) 142: { 143: this(null, text, 0); 144: } 145: 146: /** 147: * Creates a new instance of <code>JTextField</code>. 148: * 149: * @param columns the number of columns 150: * 151: * @exception IllegalArgumentException if columns %lt; 0 152: */ 153: public JTextField(int columns) 154: { 155: this(null, null, columns); 156: } 157: 158: /** 159: * Creates a new instance of <code>JTextField</code>. 160: * 161: * @param text the initial text 162: * @param columns the number of columns 163: * 164: * @exception IllegalArgumentException if columns %lt; 0 165: */ 166: public JTextField(String text, int columns) 167: { 168: this(null, text, columns); 169: } 170: 171: /** 172: * Creates a new instance of <code>JTextField</code>. 173: * 174: * @param doc the document to use 175: * @param text the initial text 176: * @param columns the number of columns 177: * 178: * @exception IllegalArgumentException if columns %lt; 0 179: */ 180: public JTextField(Document doc, String text, int columns) 181: { 182: if (columns < 0) 183: throw new IllegalArgumentException(); 184: 185: this.columns = columns; 186: 187: // Initialize the horizontal visibility model. 188: horizontalVisibility = new DefaultBoundedRangeModel(); 189: 190: setDocument(doc == null ? createDefaultModel() : doc); 191: 192: if (text != null) 193: setText(text); 194: 195: // default value for alignment 196: align = LEADING; 197: } 198: 199: /** 200: * Creates the default model for this text field. 201: * This implementation returns an instance of <code>PlainDocument</code>. 202: * 203: * @return a new instance of the default model 204: */ 205: protected Document createDefaultModel() 206: { 207: return new PlainDocument(); 208: } 209: 210: /** 211: * Sets the document to be used for this JTextField. 212: * 213: * This sets the document property <code>filterNewlines</code> to 214: * <code>true</code> and then calls the super behaviour to setup a view and 215: * revalidate the text field. 216: * 217: * @param doc the document to set 218: */ 219: public void setDocument(Document doc) 220: { 221: doc.putProperty("filterNewlines", Boolean.TRUE); 222: super.setDocument(doc); 223: } 224: 225: /** 226: * Returns the class ID for the UI. 227: * 228: * @return "TextFieldUI"; 229: */ 230: public String getUIClassID() 231: { 232: return "TextFieldUI"; 233: } 234: 235: /** 236: * Adds a new listener object to this text field. 237: * 238: * @param listener the listener to add 239: */ 240: public void addActionListener(ActionListener listener) 241: { 242: listenerList.add(ActionListener.class, listener); 243: } 244: 245: /** 246: * Removes a listener object from this text field. 247: * 248: * @param listener the listener to remove 249: */ 250: public void removeActionListener(ActionListener listener) 251: { 252: listenerList.remove(ActionListener.class, listener); 253: } 254: 255: /** 256: * Returns all registered <code>ActionListener</code> objects. 257: * 258: * @return an array of listeners 259: * 260: * @since 1.4 261: */ 262: public ActionListener[] getActionListeners() 263: { 264: return (ActionListener[]) getListeners(ActionListener.class); 265: } 266: 267: /** 268: * Sends an action event to all registered 269: * <code>ActionListener</code> objects. 270: */ 271: protected void fireActionPerformed() 272: { 273: ActionEvent event = new ActionEvent(this, 0, 274: actionCommand == null ? getText() : actionCommand); 275: ActionListener[] listeners = getActionListeners(); 276: 277: for (int index = 0; index < listeners.length; ++index) 278: listeners[index].actionPerformed(event); 279: } 280: 281: /** 282: * Returns the number of columns of this text field. 283: * 284: * @return the number of columns 285: */ 286: public int getColumns() 287: { 288: return columns; 289: } 290: 291: /** 292: * Sets the number of columns and then invalidates the layout. 293: * @param columns the number of columns 294: * @throws IllegalArgumentException if columns < 0 295: */ 296: public void setColumns(int columns) 297: { 298: if (columns < 0) 299: throw new IllegalArgumentException(); 300: 301: this.columns = columns; 302: invalidate(); 303: //FIXME: do we need this repaint call? 304: repaint(); 305: } 306: 307: /** 308: * Returns the horizontal alignment, which is one of: JTextField.LEFT, 309: * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING, 310: * JTextField.TRAILING. 311: * @return the horizontal alignment 312: */ 313: public int getHorizontalAlignment() 314: { 315: return align; 316: } 317: 318: /** 319: * Sets the horizontal alignment of the text. Calls invalidate and repaint 320: * and fires a property change event. 321: * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER, 322: * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING. 323: * @throws IllegalArgumentException if newAlign is not one of the above. 324: */ 325: public void setHorizontalAlignment(int newAlign) 326: { 327: //FIXME: should throw an IllegalArgumentException if newAlign is invalid 328: if (align == newAlign) 329: return; 330: 331: int oldAlign = align; 332: align = newAlign; 333: firePropertyChange("horizontalAlignment", oldAlign, newAlign); 334: invalidate(); 335: repaint(); 336: } 337: 338: /** 339: * Sets the current font and revalidates so the font will take effect. 340: */ 341: public void setFont(Font newFont) 342: { 343: super.setFont(newFont); 344: revalidate(); 345: } 346: 347: /** 348: * Returns the preferred size. If there is a non-zero number of columns, 349: * this is the number of columns multiplied by the column width, otherwise 350: * it returns super.getPreferredSize(). 351: */ 352: public Dimension getPreferredSize() 353: { 354: Dimension size = super.getPreferredSize(); 355: 356: if (columns != 0) 357: { 358: Insets i = getInsets(); 359: size.width = columns * getColumnWidth() + i.left + i.right; 360: } 361: 362: return size; 363: } 364: 365: /** 366: * Returns the scroll offset in pixels. 367: * 368: * @return the scroll offset 369: */ 370: public int getScrollOffset() 371: { 372: return horizontalVisibility.getValue(); 373: } 374: 375: /** 376: * Sets the scroll offset in pixels. 377: * 378: * @param offset the scroll offset 379: */ 380: public void setScrollOffset(int offset) 381: { 382: // Automatically sets to the highest possible value if 383: // offset is bigger than that. 384: horizontalVisibility.setValue( 385: Math.min(horizontalVisibility.getMaximum() 386: - horizontalVisibility.getExtent(), 387: offset)); 388: 389: } 390: 391: /** 392: * Returns the set of Actions that are commands for the editor. 393: * This is the actions supported by this editor plus the actions 394: * of the UI (returned by JTextComponent.getActions()). 395: */ 396: public Action[] getActions() 397: { 398: return TextAction.augmentList(super.getActions(), actions); 399: } 400: 401: public void postActionEvent() 402: { 403: String command = actionCommand != null ? actionCommand : getText(); 404: ActionEvent event = new ActionEvent(this, 0, command); 405: ActionListener[] listeners = getActionListeners(); 406: 407: for (int index = 0; index < listeners.length; ++index) 408: listeners[index].actionPerformed(event); 409: } 410: 411: /** 412: * @since 1.3 413: */ 414: public Action getAction() 415: { 416: return action; 417: } 418: 419: /** 420: * @since 1.3 421: */ 422: public void setAction(Action newAction) 423: { 424: if (action == newAction) 425: return; 426: 427: if (action != null) 428: { 429: removeActionListener(action); 430: action.removePropertyChangeListener(actionPropertyChangeListener); 431: actionPropertyChangeListener = null; 432: } 433: 434: Action oldAction = action; 435: action = newAction; 436: 437: if (action != null) 438: { 439: addActionListener(action); 440: actionPropertyChangeListener = createActionPropertyChangeListener(action); 441: action.addPropertyChangeListener(actionPropertyChangeListener); 442: } 443: 444: //FIXME: is this a hack? The horizontal alignment hasn't changed 445: firePropertyChange("horizontalAlignment", oldAction, newAction); 446: } 447: 448: /** 449: * Sets the command string used in action events. 450: * @since 1.3 451: */ 452: public void setActionCommand(String command) 453: { 454: actionCommand = command; 455: } 456: 457: /** 458: * @since 1.3 459: */ 460: protected PropertyChangeListener createActionPropertyChangeListener(Action action) 461: { 462: return new PropertyChangeListener() 463: { 464: public void propertyChange(PropertyChangeEvent event) 465: { 466: // Update properties "action" and "horizontalAlignment". 467: String name = event.getPropertyName(); 468: 469: if (name.equals("enabled")) 470: { 471: boolean enabled = ((Boolean) event.getNewValue()).booleanValue(); 472: JTextField.this.setEnabled(enabled); 473: } 474: else if (name.equals(Action.SHORT_DESCRIPTION)) 475: { 476: JTextField.this.setToolTipText((String) event.getNewValue()); 477: } 478: } 479: }; 480: } 481: 482: /** 483: * 484: * @since 1.3 485: */ 486: protected void configurePropertiesFromAction(Action action) 487: { 488: if (action != null) 489: { 490: setEnabled(action.isEnabled()); 491: setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION)); 492: } 493: else 494: { 495: setEnabled(true); 496: setToolTipText(null); 497: } 498: } 499: 500: /** 501: * Returns the column width, which is the width of the character m 502: * for the font in use. 503: * @return the width of the character m for the font in use. 504: */ 505: protected int getColumnWidth() 506: { 507: FontMetrics metrics = getToolkit().getFontMetrics(getFont()); 508: return metrics.charWidth('m'); 509: } 510: 511: /** 512: * Returns the accessible context associated with the <code>JTextField</code>. 513: * 514: * @return the accessible context associated with the <code>JTextField</code> 515: */ 516: public AccessibleContext getAccessibleContext() 517: { 518: if (accessibleContext == null) 519: accessibleContext = new AccessibleJTextField(); 520: return accessibleContext; 521: } 522: 523: /** 524: * Returns the bounded range model that describes the horizontal visibility 525: * of the text field in the case when the text does not fit into the 526: * available space. The actual values of this model are managed by the look 527: * and feel implementation. 528: * 529: * @return the bounded range model that describes the horizontal visibility 530: */ 531: public BoundedRangeModel getHorizontalVisibility() 532: { 533: return horizontalVisibility; 534: } 535: 536: /** 537: * Returns <code>true</code>, unless this is embedded in a 538: * <code>JViewport</code> in which case the viewport takes responsibility of 539: * validating. 540: * 541: * @return <code>true</code>, unless this is embedded in a 542: * <code>JViewport</code> in which case the viewport takes 543: * responsibility of validating 544: */ 545: public boolean isValidateRoot() 546: { 547: return ! (getParent() instanceof JViewport); 548: } 549: 550: public void scrollRectToVisible(Rectangle r) 551: { 552: int v = horizontalVisibility.getValue(); 553: 554: // The extent value is the inner width of the text field. 555: int e = horizontalVisibility.getExtent(); 556: Insets i = getInsets(); 557: 558: // The x value in the rectangle (usually) denotes the new location 559: // of the caret. We check whether the location lies inside the left or 560: // right border and scroll into the appropriate direction. 561: // The calculation has to be shifted by the BoundedRangeModel's value 562: // because that value was already used to calculate r.x (this happens 563: // as part of a modelToView() call in FieldView). 564: if (r.x < i.left) 565: setScrollOffset(v + r.x - i.left); 566: else if (r.x > e + i.left) 567: setScrollOffset(r.x + v - e - i.left); 568: } 569: 570: }