GNU Classpath (0.17) | ||
Frames | No Frames |
1: /* JTextArea.java -- 2: Copyright (C) 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.FontMetrics; 43: import java.awt.Rectangle; 44: 45: import javax.swing.text.BadLocationException; 46: import javax.swing.text.Document; 47: import javax.swing.text.Element; 48: import javax.swing.text.JTextComponent; 49: import javax.swing.text.PlainDocument; 50: import javax.swing.text.View; 51: 52: /** 53: * The <code>JTextArea</code> component provides a multi-line area for displaying 54: * and editing plain text. The component is designed to act as a lightweight 55: * replacement for the heavyweight <code>java.awt.TextArea</code> component, 56: * which provides similar functionality using native widgets. 57: * <p> 58: * 59: * This component has additional functionality to the AWT class. It follows 60: * the same design pattern as seen in other text components, such as 61: * <code>JTextField</code>, <code>JTextPane</code> and <code>JEditorPane</code>, 62: * and embodied in <code>JTextComponent</code>. These classes separate the text 63: * (the model) from its appearance within the onscreen component (the view). The 64: * text is held within a <code>javax.swing.text.Document</code> object, which can 65: * also maintain relevant style information where necessary. As a result, it is the 66: * document that should be monitored for textual changes, via 67: * <code>DocumentEvent</code>s delivered to registered 68: * <code>DocumentListener</code>s, rather than this component. 69: * <p> 70: * 71: * Unlike <code>java.awt.TextArea</code>, <code>JTextArea</code> does not 72: * handle scrolling. Instead, this functionality is delegated to a 73: * <code>JScrollPane</code>, which can contain the text area and handle 74: * scrolling when required. Likewise, the word wrapping functionality 75: * of the AWT component is converted to a property of this component 76: * and the <code>rows</code> and <code>columns</code> properties 77: * are used in calculating the preferred size of the scroll pane's 78: * view port. 79: * 80: * @author Michael Koch (konqueror@gmx.de) 81: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 82: * @see java.awt.TextArea 83: * @see javax.swing.JTextComponent 84: * @see javax.swing.JTextField 85: * @see javax.swing.JTextPane 86: * @see javax.swing.JEditorPane 87: * @see javax.swing.text.Document 88: * @see javax.swing.text.DocumentEvent 89: * @see javax.swing.text.DocumentListener 90: */ 91: 92: public class JTextArea extends JTextComponent 93: { 94: /** 95: * Compatible with Sun's JDK 96: */ 97: private static final long serialVersionUID = -6141680179310439825L; 98: 99: /** 100: * The number of rows used by the component. 101: */ 102: private int rows; 103: 104: /** 105: * The number of columns used by the component. 106: */ 107: private int columns; 108: 109: /** 110: * Whether line wrapping is enabled or not. 111: */ 112: private boolean lineWrap; 113: 114: /** 115: * The number of characters equal to a tab within the text. 116: */ 117: private int tabSize = 8; 118: 119: private boolean wrapStyleWord; 120: 121: /** 122: * Creates a new <code>JTextArea</code> object. 123: */ 124: public JTextArea() 125: { 126: this(null, null, 0, 0); 127: } 128: 129: /** 130: * Creates a new <code>JTextArea</code> object. 131: * 132: * @param text the initial text 133: */ 134: public JTextArea(String text) 135: { 136: this(null, text, 0, 0); 137: } 138: 139: /** 140: * Creates a new <code>JTextArea</code> object. 141: * 142: * @param rows the number of rows 143: * @param columns the number of cols 144: * 145: * @exception IllegalArgumentException if rows or columns are negative 146: */ 147: public JTextArea(int rows, int columns) 148: { 149: this(null, null, rows, columns); 150: } 151: 152: /** 153: * Creates a new <code>JTextArea</code> object. 154: * 155: * @param text the initial text 156: * @param rows the number of rows 157: * @param columns the number of cols 158: * 159: * @exception IllegalArgumentException if rows or columns are negative 160: */ 161: public JTextArea(String text, int rows, int columns) 162: { 163: this(null, text, rows, columns); 164: } 165: 166: /** 167: * Creates a new <code>JTextArea</code> object. 168: * 169: * @param the document model to use 170: */ 171: public JTextArea(Document doc) 172: { 173: this(doc, null, 0, 0); 174: } 175: 176: /** 177: * Creates a new <code>JTextArea</code> object. 178: * 179: * @param the document model to use 180: * @param text the initial text 181: * @param rows the number of rows 182: * @param columns the number of cols 183: * 184: * @exception IllegalArgumentException if rows or columns are negative 185: */ 186: public JTextArea(Document doc, String text, int rows, int columns) 187: { 188: setDocument(doc == null ? createDefaultModel() : doc); 189: setText(text); 190: setRows(rows); 191: setColumns(columns); 192: } 193: 194: /** 195: * Appends the supplied text to the current contents 196: * of the document model. 197: * 198: * @param toAppend the text to append 199: */ 200: public void append(String toAppend) 201: { 202: try 203: { 204: getDocument().insertString(getText().length(), toAppend, null); 205: } 206: catch (BadLocationException exception) 207: { 208: /* This shouldn't happen in theory -- but, if it does... */ 209: throw new RuntimeException("Unexpected exception occurred.", exception); 210: } 211: } 212: 213: /** 214: * Creates the default document model. 215: * 216: * @return a new default model 217: */ 218: protected Document createDefaultModel() 219: { 220: return new PlainDocument(); 221: } 222: 223: /** 224: * Returns true if the width of this component should be forced 225: * to match the width of a surrounding view port. When line wrapping 226: * is turned on, this method returns true. 227: * 228: * @return true if lines are wrapped. 229: */ 230: public boolean getScrollableTracksViewportWidth() 231: { 232: return lineWrap ? true : super.getScrollableTracksViewportWidth(); 233: } 234: 235: /** 236: * Returns the increment that is needed to expose exactly one new line 237: * of text. This is implemented here to return the values of 238: * {@link #getRowHeight} and {@link getColumnWidth}, depending on 239: * the value of the argument <code>direction</code>. 240: * 241: * @param visibleRect the view area that is visible in the viewport 242: * @param orientation either {@link SwingConstants.VERTICAL} or 243: * {@link SwingConstants.HORIZONTAL} 244: * @param direction less than zero for up/left scrolling, greater 245: * than zero for down/right scrolling 246: * 247: * @return the increment that is needed to expose exactly one new row 248: * or column of text 249: * 250: * @throws IllegalArgumentException if <code>orientation</code> is invalid 251: */ 252: public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, 253: int direction) 254: { 255: if (orientation == SwingConstants.VERTICAL) 256: return getRowHeight(); 257: else if (orientation == SwingConstants.HORIZONTAL) 258: return getColumnWidth(); 259: else 260: throw new IllegalArgumentException("orientation must be either " 261: + "javax.swing.SwingConstants.VERTICAL " 262: + "or " 263: + "javax.swing.SwingConstants.HORIZONTAL" 264: ); 265: } 266: 267: /** 268: * Returns the preferred size of that text component in the case 269: * it is embedded within a JScrollPane. This uses the column and 270: * row settings if they are explicitly set, or fall back to 271: * the superclass's behaviour. 272: * 273: * @return the preferred size of that text component in the case 274: * it is embedded within a JScrollPane 275: */ 276: public Dimension getPreferredScrollableViewportSize() 277: { 278: if ((rows > 0) && (columns > 0)) 279: return new Dimension(columns * getColumnWidth(), rows * getRowHeight()); 280: else 281: return super.getPreferredScrollableViewportSize(); 282: } 283: 284: /** 285: * Returns the UI class ID string. 286: * 287: * @return the string "TextAreaUI" 288: */ 289: public String getUIClassID() 290: { 291: return "TextAreaUI"; 292: } 293: 294: /** 295: * Returns the current number of columns. 296: * 297: * @return number of columns 298: */ 299: public int getColumns() 300: { 301: return columns; 302: } 303: 304: /** 305: * Sets the number of rows. 306: * 307: * @param columns number of columns 308: * 309: * @exception IllegalArgumentException if columns is negative 310: */ 311: public void setColumns(int columns) 312: { 313: if (columns < 0) 314: throw new IllegalArgumentException(); 315: 316: this.columns = columns; 317: } 318: 319: /** 320: * Returns the current number of rows. 321: * 322: * @return number of rows 323: */ 324: public int getRows() 325: { 326: return rows; 327: } 328: 329: /** 330: * Sets the number of rows. 331: * 332: * @param columns number of columns 333: * 334: * @exception IllegalArgumentException if rows is negative 335: */ 336: public void setRows(int rows) 337: { 338: if (rows < 0) 339: throw new IllegalArgumentException(); 340: 341: this.rows = rows; 342: } 343: 344: /** 345: * Checks whether line wrapping is enabled. 346: * 347: * @return <code>true</code> if line wrapping is enabled, 348: * <code>false</code> otherwise 349: */ 350: public boolean getLineWrap() 351: { 352: return lineWrap; 353: } 354: 355: /** 356: * Enables/disables line wrapping. 357: * 358: * @param wrapping <code>true</code> to enable line wrapping, 359: * <code>false</code> otherwise 360: */ 361: public void setLineWrap(boolean flag) 362: { 363: if (lineWrap == flag) 364: return; 365: 366: boolean oldValue = lineWrap; 367: lineWrap = flag; 368: firePropertyChange("lineWrap", oldValue, lineWrap); 369: } 370: 371: /** 372: * Checks whether word style wrapping is enabled. 373: * 374: * @return <code>true</code> if word style wrapping is enabled, 375: * <code>false</code> otherwise 376: */ 377: public boolean getWrapStyleWord() 378: { 379: return wrapStyleWord; 380: } 381: 382: /** 383: * Enables/Disables word style wrapping. 384: * 385: * @param flag <code>true</code> to enable word style wrapping, 386: * <code>false</code> otherwise 387: */ 388: public void setWrapStyleWord(boolean flag) 389: { 390: if (wrapStyleWord == flag) 391: return; 392: 393: boolean oldValue = wrapStyleWord; 394: wrapStyleWord = flag; 395: firePropertyChange("wrapStyleWord", oldValue, wrapStyleWord); 396: } 397: 398: /** 399: * Returns the number of characters used for a tab. 400: * This defaults to 8. 401: * 402: * @return the current number of spaces used for a tab. 403: */ 404: public int getTabSize() 405: { 406: return tabSize; 407: } 408: 409: /** 410: * Sets the number of characters used for a tab to the 411: * supplied value. If a change to the tab size property 412: * occurs (i.e. newSize != tabSize), a property change event 413: * is fired. 414: * 415: * @param newSize The new number of characters to use for a tab. 416: */ 417: public void setTabSize(int newSize) 418: { 419: if (tabSize == newSize) 420: return; 421: 422: int oldValue = tabSize; 423: tabSize = newSize; 424: firePropertyChange("tabSize", oldValue, tabSize); 425: } 426: 427: protected int getColumnWidth() 428: { 429: FontMetrics metrics = getToolkit().getFontMetrics(getFont()); 430: return metrics.charWidth('m'); 431: } 432: 433: public int getLineCount() 434: { 435: return getDocument().getDefaultRootElement().getElementCount(); 436: } 437: 438: public int getLineStartOffset(int line) 439: throws BadLocationException 440: { 441: int lineCount = getLineCount(); 442: 443: if (line < 0 || line > lineCount) 444: throw new BadLocationException("Non-existing line number", line); 445: 446: Element lineElem = getDocument().getDefaultRootElement().getElement(line); 447: return lineElem.getStartOffset(); 448: } 449: 450: public int getLineEndOffset(int line) 451: throws BadLocationException 452: { 453: int lineCount = getLineCount(); 454: 455: if (line < 0 || line > lineCount) 456: throw new BadLocationException("Non-existing line number", line); 457: 458: Element lineElem = getDocument().getDefaultRootElement().getElement(line); 459: return lineElem.getEndOffset(); 460: } 461: 462: public int getLineOfOffset(int offset) 463: throws BadLocationException 464: { 465: Document doc = getDocument(); 466: 467: if (offset < doc.getStartPosition().getOffset() 468: || offset >= doc.getEndPosition().getOffset()) 469: throw new BadLocationException("offset outside of document", offset); 470: 471: return doc.getDefaultRootElement().getElementIndex(offset); 472: } 473: 474: protected int getRowHeight() 475: { 476: FontMetrics metrics = getToolkit().getFontMetrics(getFont()); 477: return metrics.getHeight(); 478: } 479: 480: /** 481: * Inserts the supplied text at the specified position. Nothing 482: * happens in the case that the model or the supplied string is null 483: * or of zero length. 484: * 485: * @param string The string of text to insert. 486: * @param position The position at which to insert the supplied text. 487: * @throws IllegalArgumentException if the position is < 0 or greater 488: * than the length of the current text. 489: */ 490: public void insert(String string, int position) 491: { 492: // Retrieve the document model. 493: Document doc = getDocument(); 494: 495: // Check the model and string for validity. 496: if (doc == null 497: || string == null 498: || string.length() == 0) 499: return; 500: 501: // Insert the text into the model. 502: try 503: { 504: doc.insertString(position, string, null); 505: } 506: catch (BadLocationException e) 507: { 508: throw new IllegalArgumentException("The supplied position, " 509: + position + ", was invalid."); 510: } 511: } 512: 513: public void replaceRange(String text, int start, int end) 514: { 515: Document doc = getDocument(); 516: 517: if (start > end 518: || start < doc.getStartPosition().getOffset() 519: || end >= doc.getEndPosition().getOffset()) 520: throw new IllegalArgumentException(); 521: 522: try 523: { 524: doc.remove(start, end - start); 525: doc.insertString(start, text, null); 526: } 527: catch (BadLocationException e) 528: { 529: // This cannot happen as we check offset above. 530: } 531: } 532: 533: /** 534: * Returns the preferred size for the JTextArea. This is the maximum of 535: * the size that is needed to display the content and the requested size 536: * as per {@link #getColumns} and {@link #getRows}. 537: * 538: * @return the preferred size of the JTextArea 539: */ 540: public Dimension getPreferredSize() 541: { 542: int reqWidth = getColumns() * getColumnWidth(); 543: int reqHeight = getRows() * getRowHeight(); 544: View view = getUI().getRootView(this); 545: int neededWidth = (int) view.getPreferredSpan(View.HORIZONTAL); 546: int neededHeight = (int) view.getPreferredSpan(View.VERTICAL); 547: return new Dimension(Math.max(reqWidth, neededWidth), 548: Math.max(reqHeight, neededHeight)); 549: } 550: }
GNU Classpath (0.17) |