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, getText()); 274: ActionListener[] listeners = getActionListeners(); 275: 276: for (int index = 0; index < listeners.length; ++index) 277: listeners[index].actionPerformed(event); 278: } 279: 280: /** 281: * Returns the number of columns of this text field. 282: * 283: * @return the number of columns 284: */ 285: public int getColumns() 286: { 287: return columns; 288: } 289: 290: /** 291: * Sets the number of columns and then invalidates the layout. 292: * @param columns the number of columns 293: * @throws IllegalArgumentException if columns < 0 294: */ 295: public void setColumns(int columns) 296: { 297: if (columns < 0) 298: throw new IllegalArgumentException(); 299: 300: this.columns = columns; 301: invalidate(); 302: //FIXME: do we need this repaint call? 303: repaint(); 304: } 305: 306: /** 307: * Returns the horizontal alignment, which is one of: JTextField.LEFT, 308: * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING, 309: * JTextField.TRAILING. 310: * @return the horizontal alignment 311: */ 312: public int getHorizontalAlignment() 313: { 314: return align; 315: } 316: 317: /** 318: * Sets the horizontal alignment of the text. Calls invalidate and repaint 319: * and fires a property change event. 320: * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER, 321: * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING. 322: * @throws IllegalArgumentException if newAlign is not one of the above. 323: */ 324: public void setHorizontalAlignment(int newAlign) 325: { 326: //FIXME: should throw an IllegalArgumentException if newAlign is invalid 327: if (align == newAlign) 328: return; 329: 330: int oldAlign = align; 331: align = newAlign; 332: firePropertyChange("horizontalAlignment", oldAlign, newAlign); 333: invalidate(); 334: repaint(); 335: } 336: 337: /** 338: * Sets the current font and revalidates so the font will take effect. 339: */ 340: public void setFont(Font newFont) 341: { 342: super.setFont(newFont); 343: revalidate(); 344: } 345: 346: /** 347: * Returns the preferred size. If there is a non-zero number of columns, 348: * this is the number of columns multiplied by the column width, otherwise 349: * it returns super.getPreferredSize(). 350: */ 351: public Dimension getPreferredSize() 352: { 353: Dimension size = super.getPreferredSize(); 354: 355: if (columns != 0) 356: { 357: Insets i = getInsets(); 358: size.width = columns * getColumnWidth() + i.left + i.right; 359: } 360: 361: return size; 362: } 363: 364: /** 365: * Returns the scroll offset in pixels. 366: * 367: * @return the scroll offset 368: */ 369: public int getScrollOffset() 370: { 371: return horizontalVisibility.getValue(); 372: } 373: 374: /** 375: * Sets the scroll offset in pixels. 376: * 377: * @param offset the scroll offset 378: */ 379: public void setScrollOffset(int offset) 380: { 381: // Automatically sets to the highest possible value if 382: // offset is bigger than that. 383: horizontalVisibility.setValue( 384: Math.min(horizontalVisibility.getMaximum() 385: - horizontalVisibility.getExtent(), 386: offset)); 387: 388: } 389: 390: /** 391: * Returns the set of Actions that are commands for the editor. 392: * This is the actions supported by this editor plus the actions 393: * of the UI (returned by JTextComponent.getActions()). 394: */ 395: public Action[] getActions() 396: { 397: return TextAction.augmentList(super.getActions(), actions); 398: } 399: 400: public void postActionEvent() 401: { 402: String command = actionCommand != null ? actionCommand : getText(); 403: ActionEvent event = new ActionEvent(this, 0, command); 404: ActionListener[] listeners = getActionListeners(); 405: 406: for (int index = 0; index < listeners.length; ++index) 407: listeners[index].actionPerformed(event); 408: } 409: 410: /** 411: * @since 1.3 412: */ 413: public Action getAction() 414: { 415: return action; 416: } 417: 418: /** 419: * @since 1.3 420: */ 421: public void setAction(Action newAction) 422: { 423: if (action == newAction) 424: return; 425: 426: if (action != null) 427: { 428: removeActionListener(action); 429: action.removePropertyChangeListener(actionPropertyChangeListener); 430: actionPropertyChangeListener = null; 431: } 432: 433: Action oldAction = action; 434: action = newAction; 435: 436: if (action != null) 437: { 438: addActionListener(action); 439: actionPropertyChangeListener = createActionPropertyChangeListener(action); 440: action.addPropertyChangeListener(actionPropertyChangeListener); 441: } 442: 443: //FIXME: is this a hack? The horizontal alignment hasn't changed 444: firePropertyChange("horizontalAlignment", oldAction, newAction); 445: } 446: 447: /** 448: * Sets the command string used in action events. 449: * @since 1.3 450: */ 451: public void setActionCommand(String command) 452: { 453: actionCommand = command; 454: } 455: 456: /** 457: * @since 1.3 458: */ 459: protected PropertyChangeListener createActionPropertyChangeListener(Action action) 460: { 461: return new PropertyChangeListener() 462: { 463: public void propertyChange(PropertyChangeEvent event) 464: { 465: // Update properties "action" and "horizontalAlignment". 466: String name = event.getPropertyName(); 467: 468: if (name.equals("enabled")) 469: { 470: boolean enabled = ((Boolean) event.getNewValue()).booleanValue(); 471: JTextField.this.setEnabled(enabled); 472: } 473: else if (name.equals(Action.SHORT_DESCRIPTION)) 474: { 475: JTextField.this.setToolTipText((String) event.getNewValue()); 476: } 477: } 478: }; 479: } 480: 481: /** 482: * 483: * @since 1.3 484: */ 485: protected void configurePropertiesFromAction(Action action) 486: { 487: if (action != null) 488: { 489: setEnabled(action.isEnabled()); 490: setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION)); 491: } 492: else 493: { 494: setEnabled(true); 495: setToolTipText(null); 496: } 497: } 498: 499: /** 500: * Returns the column width, which is the width of the character m 501: * for the font in use. 502: * @return the width of the character m for the font in use. 503: */ 504: protected int getColumnWidth() 505: { 506: FontMetrics metrics = getToolkit().getFontMetrics(getFont()); 507: return metrics.charWidth('m'); 508: } 509: 510: /** 511: * Returns the accessible context associated with the <code>JTextField</code>. 512: * 513: * @return the accessible context associated with the <code>JTextField</code> 514: */ 515: public AccessibleContext getAccessibleContext() 516: { 517: if (accessibleContext == null) 518: accessibleContext = new AccessibleJTextField(); 519: return accessibleContext; 520: } 521: 522: /** 523: * Returns the bounded range model that describes the horizontal visibility 524: * of the text field in the case when the text does not fit into the 525: * available space. The actual values of this model are managed by the look 526: * and feel implementation. 527: * 528: * @return the bounded range model that describes the horizontal visibility 529: */ 530: public BoundedRangeModel getHorizontalVisibility() 531: { 532: return horizontalVisibility; 533: } 534: 535: /** 536: * Returns <code>true</code>, unless this is embedded in a 537: * <code>JViewport</code> in which case the viewport takes responsibility of 538: * validating. 539: * 540: * @return <code>true</code>, unless this is embedded in a 541: * <code>JViewport</code> in which case the viewport takes 542: * responsibility of validating 543: */ 544: public boolean isValidateRoot() 545: { 546: return ! (getParent() instanceof JViewport); 547: } 548: 549: public void scrollRectToVisible(Rectangle r) 550: { 551: int v = horizontalVisibility.getValue(); 552: 553: // The extent value is the inner width of the text field. 554: int e = horizontalVisibility.getExtent(); 555: Insets i = getInsets(); 556: 557: // The x value in the rectangle (usually) denotes the new location 558: // of the caret. We check whether the location lies inside the left or 559: // right border and scroll into the appropriate direction. 560: // The calculation has to be shifted by the BoundedRangeModel's value 561: // because that value was already used to calculate r.x (this happens 562: // as part of a modelToView() call in FieldView). 563: if (r.x < i.left) 564: setScrollOffset(v + r.x - i.left); 565: else if (r.x > e + i.left) 566: setScrollOffset(r.x + v - e - i.left); 567: } 568: 569: }