1 /*
   2  * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import javax.swing.text.*;
  30 import javax.swing.plaf.*;
  31 import javax.accessibility.*;
  32 
  33 import java.util.Collections;
  34 import java.util.Set;
  35 import java.util.StringTokenizer;
  36 
  37 import java.io.ObjectOutputStream;
  38 import java.io.ObjectInputStream;
  39 import java.io.IOException;
  40 
  41 /**
  42  * A <code>JTextArea</code> is a multi-line area that displays plain text.
  43  * It is intended to be a lightweight component that provides source
  44  * compatibility with the <code>java.awt.TextArea</code> class where it can
  45  * reasonably do so.
  46  * You can find information and examples of using all the text components in
  47  * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/text.html">Using Text Components</a>,
  48  * a section in <em>The Java Tutorial.</em>
  49  *
  50  * <p>
  51  * This component has capabilities not found in the
  52  * <code>java.awt.TextArea</code> class.  The superclass should be
  53  * consulted for additional capabilities.
  54  * Alternative multi-line text classes with
  55  * more capabilities are <code>JTextPane</code> and <code>JEditorPane</code>.
  56  * <p>
  57  * The <code>java.awt.TextArea</code> internally handles scrolling.
  58  * <code>JTextArea</code> is different in that it doesn't manage scrolling,
  59  * but implements the swing <code>Scrollable</code> interface.  This allows it
  60  * to be placed inside a <code>JScrollPane</code> if scrolling
  61  * behavior is desired, and used directly if scrolling is not desired.
  62  * <p>
  63  * The <code>java.awt.TextArea</code> has the ability to do line wrapping.
  64  * This was controlled by the horizontal scrolling policy.  Since
  65  * scrolling is not done by <code>JTextArea</code> directly, backward
  66  * compatibility must be provided another way.  <code>JTextArea</code> has
  67  * a bound property for line wrapping that controls whether or
  68  * not it will wrap lines.  By default, the line wrapping property
  69  * is set to false (not wrapped).
  70  * <p>
  71  * <code>java.awt.TextArea</code> has two properties <code>rows</code>
  72  * and <code>columns</code> that are used to determine the preferred size.
  73  * <code>JTextArea</code> uses these properties to indicate the
  74  * preferred size of the viewport when placed inside a <code>JScrollPane</code>
  75  * to match the functionality provided by <code>java.awt.TextArea</code>.
  76  * <code>JTextArea</code> has a preferred size of what is needed to
  77  * display all of the text, so that it functions properly inside of
  78  * a <code>JScrollPane</code>.  If the value for <code>rows</code>
  79  * or <code>columns</code> is equal to zero,
  80  * the preferred size along that axis is used for
  81  * the viewport preferred size along the same axis.
  82  * <p>
  83  * The <code>java.awt.TextArea</code> could be monitored for changes by adding
  84  * a <code>TextListener</code> for <code>TextEvent</code>s.
  85  * In the <code>JTextComponent</code> based
  86  * components, changes are broadcasted from the model via a
  87  * <code>DocumentEvent</code> to <code>DocumentListeners</code>.
  88  * The <code>DocumentEvent</code> gives
  89  * the location of the change and the kind of change if desired.
  90  * The code fragment might look something like:
  91  * <pre>
  92  *    DocumentListener myListener = ??;
  93  *    JTextArea myArea = ??;
  94  *    myArea.getDocument().addDocumentListener(myListener);
  95  * </pre>
  96  * <p>
  97  * <dl>
  98  * <dt><b><font size=+1>Newlines</font></b>
  99  * <dd>
 100  * For a discussion on how newlines are handled, see
 101  * <a href="text/DefaultEditorKit.html">DefaultEditorKit</a>.
 102  * </dl>
 103  *
 104  * <p>
 105  * <strong>Warning:</strong> Swing is not thread safe. For more
 106  * information see <a
 107  * href="package-summary.html#threading">Swing's Threading
 108  * Policy</a>.
 109  * <p>
 110  * <strong>Warning:</strong>
 111  * Serialized objects of this class will not be compatible with
 112  * future Swing releases. The current serialization support is
 113  * appropriate for short term storage or RMI between applications running
 114  * the same version of Swing.  As of 1.4, support for long term storage
 115  * of all JavaBeans<sup><font size="-2">TM</font></sup>
 116  * has been added to the <code>java.beans</code> package.
 117  * Please see {@link java.beans.XMLEncoder}.
 118  *
 119  * @beaninfo
 120  *   attribute: isContainer false
 121  * description: A multi-line area that displays plain text.
 122  *
 123  * @author  Timothy Prinzing
 124  * @see JTextPane
 125  * @see JEditorPane
 126  */
 127 public class JTextArea extends JTextComponent {
 128 
 129     /**
 130      * @see #getUIClassID
 131      * @see #readObject
 132      */
 133     private static final String uiClassID = "TextAreaUI";
 134 
 135     /**
 136      * Constructs a new TextArea.  A default model is set, the initial string
 137      * is null, and rows/columns are set to 0.
 138      */
 139     public JTextArea() {
 140         this(null, null, 0, 0);
 141     }
 142 
 143     /**
 144      * Constructs a new TextArea with the specified text displayed.
 145      * A default model is created and rows/columns are set to 0.
 146      *
 147      * @param text the text to be displayed, or null
 148      */
 149     public JTextArea(String text) {
 150         this(null, text, 0, 0);
 151     }
 152 
 153     /**
 154      * Constructs a new empty TextArea with the specified number of
 155      * rows and columns.  A default model is created, and the initial
 156      * string is null.
 157      *
 158      * @param rows the number of rows &gt;= 0
 159      * @param columns the number of columns &gt;= 0
 160      * @exception IllegalArgumentException if the rows or columns
 161      *  arguments are negative.
 162      */
 163     public JTextArea(int rows, int columns) {
 164         this(null, null, rows, columns);
 165     }
 166 
 167     /**
 168      * Constructs a new TextArea with the specified text and number
 169      * of rows and columns.  A default model is created.
 170      *
 171      * @param text the text to be displayed, or null
 172      * @param rows the number of rows &gt;= 0
 173      * @param columns the number of columns &gt;= 0
 174      * @exception IllegalArgumentException if the rows or columns
 175      *  arguments are negative.
 176      */
 177     public JTextArea(String text, int rows, int columns) {
 178         this(null, text, rows, columns);
 179     }
 180 
 181     /**
 182      * Constructs a new JTextArea with the given document model, and defaults
 183      * for all of the other arguments (null, 0, 0).
 184      *
 185      * @param doc  the model to use
 186      */
 187     public JTextArea(Document doc) {
 188         this(doc, null, 0, 0);
 189     }
 190 
 191     /**
 192      * Constructs a new JTextArea with the specified number of rows
 193      * and columns, and the given model.  All of the constructors
 194      * feed through this constructor.
 195      *
 196      * @param doc the model to use, or create a default one if null
 197      * @param text the text to be displayed, null if none
 198      * @param rows the number of rows &gt;= 0
 199      * @param columns the number of columns &gt;= 0
 200      * @exception IllegalArgumentException if the rows or columns
 201      *  arguments are negative.
 202      */
 203     public JTextArea(Document doc, String text, int rows, int columns) {
 204         super();
 205         this.rows = rows;
 206         this.columns = columns;
 207         if (doc == null) {
 208             doc = createDefaultModel();
 209         }
 210         setDocument(doc);
 211         if (text != null) {
 212             setText(text);
 213             select(0, 0);
 214         }
 215         if (rows < 0) {
 216             throw new IllegalArgumentException("rows: " + rows);
 217         }
 218         if (columns < 0) {
 219             throw new IllegalArgumentException("columns: " + columns);
 220         }
 221         LookAndFeel.installProperty(this,
 222                                     "focusTraversalKeysForward",
 223                                     JComponent.
 224                                     getManagingFocusForwardTraversalKeys());
 225         LookAndFeel.installProperty(this,
 226                                     "focusTraversalKeysBackward",
 227                                     JComponent.
 228                                     getManagingFocusBackwardTraversalKeys());
 229     }
 230 
 231     /**
 232      * Returns the class ID for the UI.
 233      *
 234      * @return the ID ("TextAreaUI")
 235      * @see JComponent#getUIClassID
 236      * @see UIDefaults#getUI
 237      */
 238     public String getUIClassID() {
 239         return uiClassID;
 240     }
 241 
 242     /**
 243      * Creates the default implementation of the model
 244      * to be used at construction if one isn't explicitly
 245      * given.  A new instance of PlainDocument is returned.
 246      *
 247      * @return the default document model
 248      */
 249     protected Document createDefaultModel() {
 250         return new PlainDocument();
 251     }
 252 
 253     /**
 254      * Sets the number of characters to expand tabs to.
 255      * This will be multiplied by the maximum advance for
 256      * variable width fonts.  A PropertyChange event ("tabSize") is fired
 257      * when the tab size changes.
 258      *
 259      * @param size number of characters to expand to
 260      * @see #getTabSize
 261      * @beaninfo
 262      *   preferred: true
 263      *       bound: true
 264      * description: the number of characters to expand tabs to
 265      */
 266     public void setTabSize(int size) {
 267         Document doc = getDocument();
 268         if (doc != null) {
 269             int old = getTabSize();
 270             doc.putProperty(PlainDocument.tabSizeAttribute, Integer.valueOf(size));
 271             firePropertyChange("tabSize", old, size);
 272         }
 273     }
 274 
 275     /**
 276      * Gets the number of characters used to expand tabs.  If the document is
 277      * null or doesn't have a tab setting, return a default of 8.
 278      *
 279      * @return the number of characters
 280      */
 281     public int getTabSize() {
 282         int size = 8;
 283         Document doc = getDocument();
 284         if (doc != null) {
 285             Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute);
 286             if (i != null) {
 287                 size = i.intValue();
 288             }
 289         }
 290         return size;
 291     }
 292 
 293     /**
 294      * Sets the line-wrapping policy of the text area.  If set
 295      * to true the lines will be wrapped if they are too long
 296      * to fit within the allocated width.  If set to false,
 297      * the lines will always be unwrapped.  A <code>PropertyChange</code>
 298      * event ("lineWrap") is fired when the policy is changed.
 299      * By default this property is false.
 300      *
 301      * @param wrap indicates if lines should be wrapped
 302      * @see #getLineWrap
 303      * @beaninfo
 304      *   preferred: true
 305      *       bound: true
 306      * description: should lines be wrapped
 307      */
 308     public void setLineWrap(boolean wrap) {
 309         boolean old = this.wrap;
 310         this.wrap = wrap;
 311         firePropertyChange("lineWrap", old, wrap);
 312     }
 313 
 314     /**
 315      * Gets the line-wrapping policy of the text area.  If set
 316      * to true the lines will be wrapped if they are too long
 317      * to fit within the allocated width.  If set to false,
 318      * the lines will always be unwrapped.
 319      *
 320      * @return if lines will be wrapped
 321      */
 322     public boolean getLineWrap() {
 323         return wrap;
 324     }
 325 
 326     /**
 327      * Sets the style of wrapping used if the text area is wrapping
 328      * lines.  If set to true the lines will be wrapped at word
 329      * boundaries (whitespace) if they are too long
 330      * to fit within the allocated width.  If set to false,
 331      * the lines will be wrapped at character boundaries.
 332      * By default this property is false.
 333      *
 334      * @param word indicates if word boundaries should be used
 335      *   for line wrapping
 336      * @see #getWrapStyleWord
 337      * @beaninfo
 338      *   preferred: false
 339      *       bound: true
 340      * description: should wrapping occur at word boundaries
 341      */
 342     public void setWrapStyleWord(boolean word) {
 343         boolean old = this.word;
 344         this.word = word;
 345         firePropertyChange("wrapStyleWord", old, word);
 346     }
 347 
 348     /**
 349      * Gets the style of wrapping used if the text area is wrapping
 350      * lines.  If set to true the lines will be wrapped at word
 351      * boundaries (ie whitespace) if they are too long
 352      * to fit within the allocated width.  If set to false,
 353      * the lines will be wrapped at character boundaries.
 354      *
 355      * @return if the wrap style should be word boundaries
 356      *  instead of character boundaries
 357      * @see #setWrapStyleWord
 358      */
 359     public boolean getWrapStyleWord() {
 360         return word;
 361     }
 362 
 363     /**
 364      * Translates an offset into the components text to a
 365      * line number.
 366      *
 367      * @param offset the offset &gt;= 0
 368      * @return the line number &gt;= 0
 369      * @exception BadLocationException thrown if the offset is
 370      *   less than zero or greater than the document length.
 371      */
 372     public int getLineOfOffset(int offset) throws BadLocationException {
 373         Document doc = getDocument();
 374         if (offset < 0) {
 375             throw new BadLocationException("Can't translate offset to line", -1);
 376         } else if (offset > doc.getLength()) {
 377             throw new BadLocationException("Can't translate offset to line", doc.getLength()+1);
 378         } else {
 379             Element map = getDocument().getDefaultRootElement();
 380             return map.getElementIndex(offset);
 381         }
 382     }
 383 
 384     /**
 385      * Determines the number of lines contained in the area.
 386      *
 387      * @return the number of lines &gt; 0
 388      */
 389     public int getLineCount() {
 390         Element map = getDocument().getDefaultRootElement();
 391         return map.getElementCount();
 392     }
 393 
 394     /**
 395      * Determines the offset of the start of the given line.
 396      *
 397      * @param line  the line number to translate &gt;= 0
 398      * @return the offset &gt;= 0
 399      * @exception BadLocationException thrown if the line is
 400      * less than zero or greater or equal to the number of
 401      * lines contained in the document (as reported by
 402      * getLineCount).
 403      */
 404     public int getLineStartOffset(int line) throws BadLocationException {
 405         int lineCount = getLineCount();
 406         if (line < 0) {
 407             throw new BadLocationException("Negative line", -1);
 408         } else if (line >= lineCount) {
 409             throw new BadLocationException("No such line", getDocument().getLength()+1);
 410         } else {
 411             Element map = getDocument().getDefaultRootElement();
 412             Element lineElem = map.getElement(line);
 413             return lineElem.getStartOffset();
 414         }
 415     }
 416 
 417     /**
 418      * Determines the offset of the end of the given line.
 419      *
 420      * @param line  the line &gt;= 0
 421      * @return the offset &gt;= 0
 422      * @exception BadLocationException Thrown if the line is
 423      * less than zero or greater or equal to the number of
 424      * lines contained in the document (as reported by
 425      * getLineCount).
 426      */
 427     public int getLineEndOffset(int line) throws BadLocationException {
 428         int lineCount = getLineCount();
 429         if (line < 0) {
 430             throw new BadLocationException("Negative line", -1);
 431         } else if (line >= lineCount) {
 432             throw new BadLocationException("No such line", getDocument().getLength()+1);
 433         } else {
 434             Element map = getDocument().getDefaultRootElement();
 435             Element lineElem = map.getElement(line);
 436             int endOffset = lineElem.getEndOffset();
 437             // hide the implicit break at the end of the document
 438             return ((line == lineCount - 1) ? (endOffset - 1) : endOffset);
 439         }
 440     }
 441 
 442     // --- java.awt.TextArea methods ---------------------------------
 443 
 444     /**
 445      * Inserts the specified text at the specified position.  Does nothing
 446      * if the model is null or if the text is null or empty.
 447      *
 448      * @param str the text to insert
 449      * @param pos the position at which to insert &gt;= 0
 450      * @exception IllegalArgumentException  if pos is an
 451      *  invalid position in the model
 452      * @see TextComponent#setText
 453      * @see #replaceRange
 454      */
 455     public void insert(String str, int pos) {
 456         Document doc = getDocument();
 457         if (doc != null) {
 458             try {
 459                 doc.insertString(pos, str, null);
 460             } catch (BadLocationException e) {
 461                 throw new IllegalArgumentException(e.getMessage());
 462             }
 463         }
 464     }
 465 
 466     /**
 467      * Appends the given text to the end of the document.  Does nothing if
 468      * the model is null or the string is null or empty.
 469      *
 470      * @param str the text to insert
 471      * @see #insert
 472      */
 473     public void append(String str) {
 474         Document doc = getDocument();
 475         if (doc != null) {
 476             try {
 477                 doc.insertString(doc.getLength(), str, null);
 478             } catch (BadLocationException e) {
 479             }
 480         }
 481     }
 482 
 483     /**
 484      * Replaces text from the indicated start to end position with the
 485      * new text specified.  Does nothing if the model is null.  Simply
 486      * does a delete if the new string is null or empty.
 487      *
 488      * @param str the text to use as the replacement
 489      * @param start the start position &gt;= 0
 490      * @param end the end position &gt;= start
 491      * @exception IllegalArgumentException  if part of the range is an
 492      *  invalid position in the model
 493      * @see #insert
 494      * @see #replaceRange
 495      */
 496     public void replaceRange(String str, int start, int end) {
 497         if (end < start) {
 498             throw new IllegalArgumentException("end before start");
 499         }
 500         Document doc = getDocument();
 501         if (doc != null) {
 502             try {
 503                 if (doc instanceof AbstractDocument) {
 504                     ((AbstractDocument)doc).replace(start, end - start, str,
 505                                                     null);
 506                 }
 507                 else {
 508                     doc.remove(start, end - start);
 509                     doc.insertString(start, str, null);
 510                 }
 511             } catch (BadLocationException e) {
 512                 throw new IllegalArgumentException(e.getMessage());
 513             }
 514         }
 515     }
 516 
 517     /**
 518      * Returns the number of rows in the TextArea.
 519      *
 520      * @return the number of rows &gt;= 0
 521      */
 522     public int getRows() {
 523         return rows;
 524     }
 525 
 526     /**
 527      * Sets the number of rows for this TextArea.  Calls invalidate() after
 528      * setting the new value.
 529      *
 530      * @param rows the number of rows &gt;= 0
 531      * @exception IllegalArgumentException if rows is less than 0
 532      * @see #getRows
 533      * @beaninfo
 534      * description: the number of rows preferred for display
 535      */
 536     public void setRows(int rows) {
 537         int oldVal = this.rows;
 538         if (rows < 0) {
 539             throw new IllegalArgumentException("rows less than zero.");
 540         }
 541         if (rows != oldVal) {
 542             this.rows = rows;
 543             invalidate();
 544         }
 545     }
 546 
 547     /**
 548      * Defines the meaning of the height of a row.  This defaults to
 549      * the height of the font.
 550      *
 551      * @return the height &gt;= 1
 552      */
 553     protected int getRowHeight() {
 554         if (rowHeight == 0) {
 555             FontMetrics metrics = getFontMetrics(getFont());
 556             rowHeight = metrics.getHeight();
 557         }
 558         return rowHeight;
 559     }
 560 
 561     /**
 562      * Returns the number of columns in the TextArea.
 563      *
 564      * @return number of columns &gt;= 0
 565      */
 566     public int getColumns() {
 567         return columns;
 568     }
 569 
 570     /**
 571      * Sets the number of columns for this TextArea.  Does an invalidate()
 572      * after setting the new value.
 573      *
 574      * @param columns the number of columns &gt;= 0
 575      * @exception IllegalArgumentException if columns is less than 0
 576      * @see #getColumns
 577      * @beaninfo
 578      * description: the number of columns preferred for display
 579      */
 580     public void setColumns(int columns) {
 581         int oldVal = this.columns;
 582         if (columns < 0) {
 583             throw new IllegalArgumentException("columns less than zero.");
 584         }
 585         if (columns != oldVal) {
 586             this.columns = columns;
 587             invalidate();
 588         }
 589     }
 590 
 591     /**
 592      * Gets column width.
 593      * The meaning of what a column is can be considered a fairly weak
 594      * notion for some fonts.  This method is used to define the width
 595      * of a column.  By default this is defined to be the width of the
 596      * character <em>m</em> for the font used.  This method can be
 597      * redefined to be some alternative amount.
 598      *
 599      * @return the column width &gt;= 1
 600      */
 601     protected int getColumnWidth() {
 602         if (columnWidth == 0) {
 603             FontMetrics metrics = getFontMetrics(getFont());
 604             columnWidth = metrics.charWidth('m');
 605         }
 606         return columnWidth;
 607     }
 608 
 609     // --- Component methods -----------------------------------------
 610 
 611     /**
 612      * Returns the preferred size of the TextArea.  This is the
 613      * maximum of the size needed to display the text and the
 614      * size requested for the viewport.
 615      *
 616      * @return the size
 617      */
 618     public Dimension getPreferredSize() {
 619         Dimension d = super.getPreferredSize();
 620         d = (d == null) ? new Dimension(400,400) : d;
 621         Insets insets = getInsets();
 622 
 623         if (columns != 0) {
 624             d.width = Math.max(d.width, columns * getColumnWidth() +
 625                     insets.left + insets.right);
 626         }
 627         if (rows != 0) {
 628             d.height = Math.max(d.height, rows * getRowHeight() +
 629                                 insets.top + insets.bottom);
 630         }
 631         return d;
 632     }
 633 
 634     /**
 635      * Sets the current font.  This removes cached row height and column
 636      * width so the new font will be reflected, and calls revalidate().
 637      *
 638      * @param f the font to use as the current font
 639      */
 640     public void setFont(Font f) {
 641         super.setFont(f);
 642         rowHeight = 0;
 643         columnWidth = 0;
 644     }
 645 
 646 
 647     /**
 648      * Returns a string representation of this JTextArea. This method
 649      * is intended to be used only for debugging purposes, and the
 650      * content and format of the returned string may vary between
 651      * implementations. The returned string may be empty but may not
 652      * be <code>null</code>.
 653      *
 654      * @return  a string representation of this JTextArea.
 655      */
 656     protected String paramString() {
 657         String wrapString = (wrap ?
 658                              "true" : "false");
 659         String wordString = (word ?
 660                              "true" : "false");
 661 
 662         return super.paramString() +
 663         ",colums=" + columns +
 664         ",columWidth=" + columnWidth +
 665         ",rows=" + rows +
 666         ",rowHeight=" + rowHeight +
 667         ",word=" + wordString +
 668         ",wrap=" + wrapString;
 669     }
 670 
 671     // --- Scrollable methods ----------------------------------------
 672 
 673     /**
 674      * Returns true if a viewport should always force the width of this
 675      * Scrollable to match the width of the viewport.  This is implemented
 676      * to return true if the line wrapping policy is true, and false
 677      * if lines are not being wrapped.
 678      *
 679      * @return true if a viewport should force the Scrollables width
 680      * to match its own.
 681      */
 682     public boolean getScrollableTracksViewportWidth() {
 683         return (wrap) ? true : super.getScrollableTracksViewportWidth();
 684     }
 685 
 686     /**
 687      * Returns the preferred size of the viewport if this component
 688      * is embedded in a JScrollPane.  This uses the desired column
 689      * and row settings if they have been set, otherwise the superclass
 690      * behavior is used.
 691      *
 692      * @return The preferredSize of a JViewport whose view is this Scrollable.
 693      * @see JViewport#getPreferredSize
 694      */
 695     public Dimension getPreferredScrollableViewportSize() {
 696         Dimension size = super.getPreferredScrollableViewportSize();
 697         size = (size == null) ? new Dimension(400,400) : size;
 698         Insets insets = getInsets();
 699 
 700         size.width = (columns == 0) ? size.width :
 701                 columns * getColumnWidth() + insets.left + insets.right;
 702         size.height = (rows == 0) ? size.height :
 703                 rows * getRowHeight() + insets.top + insets.bottom;
 704         return size;
 705     }
 706 
 707     /**
 708      * Components that display logical rows or columns should compute
 709      * the scroll increment that will completely expose one new row
 710      * or column, depending on the value of orientation.  This is implemented
 711      * to use the values returned by the <code>getRowHeight</code> and
 712      * <code>getColumnWidth</code> methods.
 713      * <p>
 714      * Scrolling containers, like JScrollPane, will use this method
 715      * each time the user requests a unit scroll.
 716      *
 717      * @param visibleRect the view area visible within the viewport
 718      * @param orientation Either SwingConstants.VERTICAL or
 719      *   SwingConstants.HORIZONTAL.
 720      * @param direction Less than zero to scroll up/left,
 721      *   greater than zero for down/right.
 722      * @return The "unit" increment for scrolling in the specified direction
 723      * @exception IllegalArgumentException for an invalid orientation
 724      * @see JScrollBar#setUnitIncrement
 725      * @see #getRowHeight
 726      * @see #getColumnWidth
 727      */
 728     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
 729         switch (orientation) {
 730         case SwingConstants.VERTICAL:
 731             return getRowHeight();
 732         case SwingConstants.HORIZONTAL:
 733             return getColumnWidth();
 734         default:
 735             throw new IllegalArgumentException("Invalid orientation: " + orientation);
 736         }
 737     }
 738 
 739     /**
 740      * See readObject() and writeObject() in JComponent for more
 741      * information about serialization in Swing.
 742      */
 743     private void writeObject(ObjectOutputStream s) throws IOException {
 744         s.defaultWriteObject();
 745         if (getUIClassID().equals(uiClassID)) {
 746             byte count = JComponent.getWriteObjCounter(this);
 747             JComponent.setWriteObjCounter(this, --count);
 748             if (count == 0 && ui != null) {
 749                 ui.installUI(this);
 750             }
 751         }
 752     }
 753 
 754 /////////////////
 755 // Accessibility support
 756 ////////////////
 757 
 758 
 759     /**
 760      * Gets the AccessibleContext associated with this JTextArea.
 761      * For JTextAreas, the AccessibleContext takes the form of an
 762      * AccessibleJTextArea.
 763      * A new AccessibleJTextArea instance is created if necessary.
 764      *
 765      * @return an AccessibleJTextArea that serves as the
 766      *         AccessibleContext of this JTextArea
 767      */
 768     public AccessibleContext getAccessibleContext() {
 769         if (accessibleContext == null) {
 770             accessibleContext = new AccessibleJTextArea();
 771         }
 772         return accessibleContext;
 773     }
 774 
 775     /**
 776      * This class implements accessibility support for the
 777      * <code>JTextArea</code> class.  It provides an implementation of the
 778      * Java Accessibility API appropriate to text area user-interface
 779      * elements.
 780      * <p>
 781      * <strong>Warning:</strong>
 782      * Serialized objects of this class will not be compatible with
 783      * future Swing releases. The current serialization support is
 784      * appropriate for short term storage or RMI between applications running
 785      * the same version of Swing.  As of 1.4, support for long term storage
 786      * of all JavaBeans<sup><font size="-2">TM</font></sup>
 787      * has been added to the <code>java.beans</code> package.
 788      * Please see {@link java.beans.XMLEncoder}.
 789      */
 790     protected class AccessibleJTextArea extends AccessibleJTextComponent {
 791 
 792         /**
 793          * Gets the state set of this object.
 794          *
 795          * @return an instance of AccessibleStateSet describing the states
 796          * of the object
 797          * @see AccessibleStateSet
 798          */
 799         public AccessibleStateSet getAccessibleStateSet() {
 800             AccessibleStateSet states = super.getAccessibleStateSet();
 801             states.add(AccessibleState.MULTI_LINE);
 802             return states;
 803         }
 804     }
 805 
 806     // --- variables -------------------------------------------------
 807 
 808     private int rows;
 809     private int columns;
 810     private int columnWidth;
 811     private int rowHeight;
 812     private boolean wrap;
 813     private boolean word;
 814 
 815 }