1 /*
   2  * Copyright (c) 1997, 2013, 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://docs.oracle.com/javase/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  *
  97  * <dl>
  98  * <dt><b>Newlines</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&trade;
 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 @SuppressWarnings("serial") // Same-version serialization only
 128 public class JTextArea extends JTextComponent {
 129 
 130     /**
 131      * @see #getUIClassID
 132      * @see #readObject
 133      */
 134     private static final String uiClassID = "TextAreaUI";
 135 
 136     /**
 137      * Constructs a new TextArea.  A default model is set, the initial string
 138      * is null, and rows/columns are set to 0.
 139      */
 140     public JTextArea() {
 141         this(null, null, 0, 0);
 142     }
 143 
 144     /**
 145      * Constructs a new TextArea with the specified text displayed.
 146      * A default model is created and rows/columns are set to 0.
 147      *
 148      * @param text the text to be displayed, or null
 149      */
 150     public JTextArea(String text) {
 151         this(null, text, 0, 0);
 152     }
 153 
 154     /**
 155      * Constructs a new empty TextArea with the specified number of
 156      * rows and columns.  A default model is created, and the initial
 157      * string is null.
 158      *
 159      * @param rows the number of rows &gt;= 0
 160      * @param columns the number of columns &gt;= 0
 161      * @exception IllegalArgumentException if the rows or columns
 162      *  arguments are negative.
 163      */
 164     public JTextArea(int rows, int columns) {
 165         this(null, null, rows, columns);
 166     }
 167 
 168     /**
 169      * Constructs a new TextArea with the specified text and number
 170      * of rows and columns.  A default model is created.
 171      *
 172      * @param text the text to be displayed, or null
 173      * @param rows the number of rows &gt;= 0
 174      * @param columns the number of columns &gt;= 0
 175      * @exception IllegalArgumentException if the rows or columns
 176      *  arguments are negative.
 177      */
 178     public JTextArea(String text, int rows, int columns) {
 179         this(null, text, rows, columns);
 180     }
 181 
 182     /**
 183      * Constructs a new JTextArea with the given document model, and defaults
 184      * for all of the other arguments (null, 0, 0).
 185      *
 186      * @param doc  the model to use
 187      */
 188     public JTextArea(Document doc) {
 189         this(doc, null, 0, 0);
 190     }
 191 
 192     /**
 193      * Constructs a new JTextArea with the specified number of rows
 194      * and columns, and the given model.  All of the constructors
 195      * feed through this constructor.
 196      *
 197      * @param doc the model to use, or create a default one if null
 198      * @param text the text to be displayed, null if none
 199      * @param rows the number of rows &gt;= 0
 200      * @param columns the number of columns &gt;= 0
 201      * @exception IllegalArgumentException if the rows or columns
 202      *  arguments are negative.
 203      */
 204     public JTextArea(Document doc, String text, int rows, int columns) {
 205         super();
 206         this.rows = rows;
 207         this.columns = columns;
 208         if (doc == null) {
 209             doc = createDefaultModel();
 210         }
 211         setDocument(doc);
 212         if (text != null) {
 213             setText(text);
 214             select(0, 0);
 215         }
 216         if (rows < 0) {
 217             throw new IllegalArgumentException("rows: " + rows);
 218         }
 219         if (columns < 0) {
 220             throw new IllegalArgumentException("columns: " + columns);
 221         }
 222         LookAndFeel.installProperty(this,
 223                                     "focusTraversalKeysForward",
 224                                     JComponent.
 225                                     getManagingFocusForwardTraversalKeys());
 226         LookAndFeel.installProperty(this,
 227                                     "focusTraversalKeysBackward",
 228                                     JComponent.
 229                                     getManagingFocusBackwardTraversalKeys());
 230     }
 231 
 232     /**
 233      * Returns the class ID for the UI.
 234      *
 235      * @return the ID ("TextAreaUI")
 236      * @see JComponent#getUIClassID
 237      * @see UIDefaults#getUI
 238      */
 239     public String getUIClassID() {
 240         return uiClassID;
 241     }
 242 
 243     /**
 244      * Creates the default implementation of the model
 245      * to be used at construction if one isn't explicitly
 246      * given.  A new instance of PlainDocument is returned.
 247      *
 248      * @return the default document model
 249      */
 250     protected Document createDefaultModel() {
 251         return new PlainDocument();
 252     }
 253 
 254     /**
 255      * Sets the number of characters to expand tabs to.
 256      * This will be multiplied by the maximum advance for
 257      * variable width fonts.  A PropertyChange event ("tabSize") is fired
 258      * when the tab size changes.
 259      *
 260      * @param size number of characters to expand to
 261      * @see #getTabSize
 262      * @beaninfo
 263      *   preferred: true
 264      *       bound: true
 265      * description: the number of characters to expand tabs to
 266      */
 267     public void setTabSize(int size) {
 268         Document doc = getDocument();
 269         if (doc != null) {
 270             int old = getTabSize();
 271             doc.putProperty(PlainDocument.tabSizeAttribute, Integer.valueOf(size));
 272             firePropertyChange("tabSize", old, size);
 273         }
 274     }
 275 
 276     /**
 277      * Gets the number of characters used to expand tabs.  If the document is
 278      * null or doesn't have a tab setting, return a default of 8.
 279      *
 280      * @return the number of characters
 281      */
 282     public int getTabSize() {
 283         int size = 8;
 284         Document doc = getDocument();
 285         if (doc != null) {
 286             Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute);
 287             if (i != null) {
 288                 size = i.intValue();
 289             }
 290         }
 291         return size;
 292     }
 293 
 294     /**
 295      * Sets the line-wrapping policy of the text area.  If set
 296      * to true the lines will be wrapped if they are too long
 297      * to fit within the allocated width.  If set to false,
 298      * the lines will always be unwrapped.  A <code>PropertyChange</code>
 299      * event ("lineWrap") is fired when the policy is changed.
 300      * By default this property is false.
 301      *
 302      * @param wrap indicates if lines should be wrapped
 303      * @see #getLineWrap
 304      * @beaninfo
 305      *   preferred: true
 306      *       bound: true
 307      * description: should lines be wrapped
 308      */
 309     public void setLineWrap(boolean wrap) {
 310         boolean old = this.wrap;
 311         this.wrap = wrap;
 312         firePropertyChange("lineWrap", old, wrap);
 313     }
 314 
 315     /**
 316      * Gets the line-wrapping policy of the text area.  If set
 317      * to true the lines will be wrapped if they are too long
 318      * to fit within the allocated width.  If set to false,
 319      * the lines will always be unwrapped.
 320      *
 321      * @return if lines will be wrapped
 322      */
 323     public boolean getLineWrap() {
 324         return wrap;
 325     }
 326 
 327     /**
 328      * Sets the style of wrapping used if the text area is wrapping
 329      * lines.  If set to true the lines will be wrapped at word
 330      * boundaries (whitespace) if they are too long
 331      * to fit within the allocated width.  If set to false,
 332      * the lines will be wrapped at character boundaries.
 333      * By default this property is false.
 334      *
 335      * @param word indicates if word boundaries should be used
 336      *   for line wrapping
 337      * @see #getWrapStyleWord
 338      * @beaninfo
 339      *   preferred: false
 340      *       bound: true
 341      * description: should wrapping occur at word boundaries
 342      */
 343     public void setWrapStyleWord(boolean word) {
 344         boolean old = this.word;
 345         this.word = word;
 346         firePropertyChange("wrapStyleWord", old, word);
 347     }
 348 
 349     /**
 350      * Gets the style of wrapping used if the text area is wrapping
 351      * lines.  If set to true the lines will be wrapped at word
 352      * boundaries (ie whitespace) if they are too long
 353      * to fit within the allocated width.  If set to false,
 354      * the lines will be wrapped at character boundaries.
 355      *
 356      * @return if the wrap style should be word boundaries
 357      *  instead of character boundaries
 358      * @see #setWrapStyleWord
 359      */
 360     public boolean getWrapStyleWord() {
 361         return word;
 362     }
 363 
 364     /**
 365      * Translates an offset into the components text to a
 366      * line number.
 367      *
 368      * @param offset the offset &gt;= 0
 369      * @return the line number &gt;= 0
 370      * @exception BadLocationException thrown if the offset is
 371      *   less than zero or greater than the document length.
 372      */
 373     public int getLineOfOffset(int offset) throws BadLocationException {
 374         Document doc = getDocument();
 375         if (offset < 0) {
 376             throw new BadLocationException("Can't translate offset to line", -1);
 377         } else if (offset > doc.getLength()) {
 378             throw new BadLocationException("Can't translate offset to line", doc.getLength()+1);
 379         } else {
 380             Element map = getDocument().getDefaultRootElement();
 381             return map.getElementIndex(offset);
 382         }
 383     }
 384 
 385     /**
 386      * Determines the number of lines contained in the area.
 387      *
 388      * @return the number of lines &gt; 0
 389      */
 390     public int getLineCount() {
 391         Element map = getDocument().getDefaultRootElement();
 392         return map.getElementCount();
 393     }
 394 
 395     /**
 396      * Determines the offset of the start of the given line.
 397      *
 398      * @param line  the line number to translate &gt;= 0
 399      * @return the offset &gt;= 0
 400      * @exception BadLocationException thrown if the line is
 401      * less than zero or greater or equal to the number of
 402      * lines contained in the document (as reported by
 403      * getLineCount).
 404      */
 405     public int getLineStartOffset(int line) throws BadLocationException {
 406         int lineCount = getLineCount();
 407         if (line < 0) {
 408             throw new BadLocationException("Negative line", -1);
 409         } else if (line >= lineCount) {
 410             throw new BadLocationException("No such line", getDocument().getLength()+1);
 411         } else {
 412             Element map = getDocument().getDefaultRootElement();
 413             Element lineElem = map.getElement(line);
 414             return lineElem.getStartOffset();
 415         }
 416     }
 417 
 418     /**
 419      * Determines the offset of the end of the given line.
 420      *
 421      * @param line  the line &gt;= 0
 422      * @return the offset &gt;= 0
 423      * @exception BadLocationException Thrown if the line is
 424      * less than zero or greater or equal to the number of
 425      * lines contained in the document (as reported by
 426      * getLineCount).
 427      */
 428     public int getLineEndOffset(int line) throws BadLocationException {
 429         int lineCount = getLineCount();
 430         if (line < 0) {
 431             throw new BadLocationException("Negative line", -1);
 432         } else if (line >= lineCount) {
 433             throw new BadLocationException("No such line", getDocument().getLength()+1);
 434         } else {
 435             Element map = getDocument().getDefaultRootElement();
 436             Element lineElem = map.getElement(line);
 437             int endOffset = lineElem.getEndOffset();
 438             // hide the implicit break at the end of the document
 439             return ((line == lineCount - 1) ? (endOffset - 1) : endOffset);
 440         }
 441     }
 442 
 443     // --- java.awt.TextArea methods ---------------------------------
 444 
 445     /**
 446      * Inserts the specified text at the specified position.  Does nothing
 447      * if the model is null or if the text is null or empty.
 448      *
 449      * @param str the text to insert
 450      * @param pos the position at which to insert &gt;= 0
 451      * @exception IllegalArgumentException  if pos is an
 452      *  invalid position in the model
 453      * @see TextComponent#setText
 454      * @see #replaceRange
 455      */
 456     public void insert(String str, int pos) {
 457         Document doc = getDocument();
 458         if (doc != null) {
 459             try {
 460                 doc.insertString(pos, str, null);
 461             } catch (BadLocationException e) {
 462                 throw new IllegalArgumentException(e.getMessage());
 463             }
 464         }
 465     }
 466 
 467     /**
 468      * Appends the given text to the end of the document.  Does nothing if
 469      * the model is null or the string is null or empty.
 470      *
 471      * @param str the text to insert
 472      * @see #insert
 473      */
 474     public void append(String str) {
 475         Document doc = getDocument();
 476         if (doc != null) {
 477             try {
 478                 doc.insertString(doc.getLength(), str, null);
 479             } catch (BadLocationException e) {
 480             }
 481         }
 482     }
 483 
 484     /**
 485      * Replaces text from the indicated start to end position with the
 486      * new text specified.  Does nothing if the model is null.  Simply
 487      * does a delete if the new string is null or empty.
 488      *
 489      * @param str the text to use as the replacement
 490      * @param start the start position &gt;= 0
 491      * @param end the end position &gt;= start
 492      * @exception IllegalArgumentException  if part of the range is an
 493      *  invalid position in the model
 494      * @see #insert
 495      * @see #replaceRange
 496      */
 497     public void replaceRange(String str, int start, int end) {
 498         if (end < start) {
 499             throw new IllegalArgumentException("end before start");
 500         }
 501         Document doc = getDocument();
 502         if (doc != null) {
 503             try {
 504                 if (doc instanceof AbstractDocument) {
 505                     ((AbstractDocument)doc).replace(start, end - start, str,
 506                                                     null);
 507                 }
 508                 else {
 509                     doc.remove(start, end - start);
 510                     doc.insertString(start, str, null);
 511                 }
 512             } catch (BadLocationException e) {
 513                 throw new IllegalArgumentException(e.getMessage());
 514             }
 515         }
 516     }
 517 
 518     /**
 519      * Returns the number of rows in the TextArea.
 520      *
 521      * @return the number of rows &gt;= 0
 522      */
 523     public int getRows() {
 524         return rows;
 525     }
 526 
 527     /**
 528      * Sets the number of rows for this TextArea.  Calls invalidate() after
 529      * setting the new value.
 530      *
 531      * @param rows the number of rows &gt;= 0
 532      * @exception IllegalArgumentException if rows is less than 0
 533      * @see #getRows
 534      * @beaninfo
 535      * description: the number of rows preferred for display
 536      */
 537     public void setRows(int rows) {
 538         int oldVal = this.rows;
 539         if (rows < 0) {
 540             throw new IllegalArgumentException("rows less than zero.");
 541         }
 542         if (rows != oldVal) {
 543             this.rows = rows;
 544             invalidate();
 545         }
 546     }
 547 
 548     /**
 549      * Defines the meaning of the height of a row.  This defaults to
 550      * the height of the font.
 551      *
 552      * @return the height &gt;= 1
 553      */
 554     protected int getRowHeight() {
 555         if (rowHeight == 0) {
 556             FontMetrics metrics = getFontMetrics(getFont());
 557             rowHeight = metrics.getHeight();
 558         }
 559         return rowHeight;
 560     }
 561 
 562     /**
 563      * Returns the number of columns in the TextArea.
 564      *
 565      * @return number of columns &gt;= 0
 566      */
 567     public int getColumns() {
 568         return columns;
 569     }
 570 
 571     /**
 572      * Sets the number of columns for this TextArea.  Does an invalidate()
 573      * after setting the new value.
 574      *
 575      * @param columns the number of columns &gt;= 0
 576      * @exception IllegalArgumentException if columns is less than 0
 577      * @see #getColumns
 578      * @beaninfo
 579      * description: the number of columns preferred for display
 580      */
 581     public void setColumns(int columns) {
 582         int oldVal = this.columns;
 583         if (columns < 0) {
 584             throw new IllegalArgumentException("columns less than zero.");
 585         }
 586         if (columns != oldVal) {
 587             this.columns = columns;
 588             invalidate();
 589         }
 590     }
 591 
 592     /**
 593      * Gets column width.
 594      * The meaning of what a column is can be considered a fairly weak
 595      * notion for some fonts.  This method is used to define the width
 596      * of a column.  By default this is defined to be the width of the
 597      * character <em>m</em> for the font used.  This method can be
 598      * redefined to be some alternative amount.
 599      *
 600      * @return the column width &gt;= 1
 601      */
 602     protected int getColumnWidth() {
 603         if (columnWidth == 0) {
 604             FontMetrics metrics = getFontMetrics(getFont());
 605             columnWidth = metrics.charWidth('m');
 606         }
 607         return columnWidth;
 608     }
 609 
 610     // --- Component methods -----------------------------------------
 611 
 612     /**
 613      * Returns the preferred size of the TextArea.  This is the
 614      * maximum of the size needed to display the text and the
 615      * size requested for the viewport.
 616      *
 617      * @return the size
 618      */
 619     public Dimension getPreferredSize() {
 620         Dimension d = super.getPreferredSize();
 621         d = (d == null) ? new Dimension(400,400) : d;
 622         Insets insets = getInsets();
 623 
 624         if (columns != 0) {
 625             d.width = Math.max(d.width, columns * getColumnWidth() +
 626                     insets.left + insets.right);
 627         }
 628         if (rows != 0) {
 629             d.height = Math.max(d.height, rows * getRowHeight() +
 630                                 insets.top + insets.bottom);
 631         }
 632         return d;
 633     }
 634 
 635     /**
 636      * Sets the current font.  This removes cached row height and column
 637      * width so the new font will be reflected, and calls revalidate().
 638      *
 639      * @param f the font to use as the current font
 640      */
 641     public void setFont(Font f) {
 642         super.setFont(f);
 643         rowHeight = 0;
 644         columnWidth = 0;
 645     }
 646 
 647 
 648     /**
 649      * Returns a string representation of this JTextArea. This method
 650      * is intended to be used only for debugging purposes, and the
 651      * content and format of the returned string may vary between
 652      * implementations. The returned string may be empty but may not
 653      * be <code>null</code>.
 654      *
 655      * @return  a string representation of this JTextArea.
 656      */
 657     protected String paramString() {
 658         String wrapString = (wrap ?
 659                              "true" : "false");
 660         String wordString = (word ?
 661                              "true" : "false");
 662 
 663         return super.paramString() +
 664         ",colums=" + columns +
 665         ",columWidth=" + columnWidth +
 666         ",rows=" + rows +
 667         ",rowHeight=" + rowHeight +
 668         ",word=" + wordString +
 669         ",wrap=" + wrapString;
 670     }
 671 
 672     // --- Scrollable methods ----------------------------------------
 673 
 674     /**
 675      * Returns true if a viewport should always force the width of this
 676      * Scrollable to match the width of the viewport.  This is implemented
 677      * to return true if the line wrapping policy is true, and false
 678      * if lines are not being wrapped.
 679      *
 680      * @return true if a viewport should force the Scrollables width
 681      * to match its own.
 682      */
 683     public boolean getScrollableTracksViewportWidth() {
 684         return (wrap) ? true : super.getScrollableTracksViewportWidth();
 685     }
 686 
 687     /**
 688      * Returns the preferred size of the viewport if this component
 689      * is embedded in a JScrollPane.  This uses the desired column
 690      * and row settings if they have been set, otherwise the superclass
 691      * behavior is used.
 692      *
 693      * @return The preferredSize of a JViewport whose view is this Scrollable.
 694      * @see JViewport#getPreferredSize
 695      */
 696     public Dimension getPreferredScrollableViewportSize() {
 697         Dimension size = super.getPreferredScrollableViewportSize();
 698         size = (size == null) ? new Dimension(400,400) : size;
 699         Insets insets = getInsets();
 700 
 701         size.width = (columns == 0) ? size.width :
 702                 columns * getColumnWidth() + insets.left + insets.right;
 703         size.height = (rows == 0) ? size.height :
 704                 rows * getRowHeight() + insets.top + insets.bottom;
 705         return size;
 706     }
 707 
 708     /**
 709      * Components that display logical rows or columns should compute
 710      * the scroll increment that will completely expose one new row
 711      * or column, depending on the value of orientation.  This is implemented
 712      * to use the values returned by the <code>getRowHeight</code> and
 713      * <code>getColumnWidth</code> methods.
 714      * <p>
 715      * Scrolling containers, like JScrollPane, will use this method
 716      * each time the user requests a unit scroll.
 717      *
 718      * @param visibleRect the view area visible within the viewport
 719      * @param orientation Either SwingConstants.VERTICAL or
 720      *   SwingConstants.HORIZONTAL.
 721      * @param direction Less than zero to scroll up/left,
 722      *   greater than zero for down/right.
 723      * @return The "unit" increment for scrolling in the specified direction
 724      * @exception IllegalArgumentException for an invalid orientation
 725      * @see JScrollBar#setUnitIncrement
 726      * @see #getRowHeight
 727      * @see #getColumnWidth
 728      */
 729     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
 730         switch (orientation) {
 731         case SwingConstants.VERTICAL:
 732             return getRowHeight();
 733         case SwingConstants.HORIZONTAL:
 734             return getColumnWidth();
 735         default:
 736             throw new IllegalArgumentException("Invalid orientation: " + orientation);
 737         }
 738     }
 739 
 740     /**
 741      * See readObject() and writeObject() in JComponent for more
 742      * information about serialization in Swing.
 743      */
 744     private void writeObject(ObjectOutputStream s) throws IOException {
 745         s.defaultWriteObject();
 746         if (getUIClassID().equals(uiClassID)) {
 747             byte count = JComponent.getWriteObjCounter(this);
 748             JComponent.setWriteObjCounter(this, --count);
 749             if (count == 0 && ui != null) {
 750                 ui.installUI(this);
 751             }
 752         }
 753     }
 754 
 755 /////////////////
 756 // Accessibility support
 757 ////////////////
 758 
 759 
 760     /**
 761      * Gets the AccessibleContext associated with this JTextArea.
 762      * For JTextAreas, the AccessibleContext takes the form of an
 763      * AccessibleJTextArea.
 764      * A new AccessibleJTextArea instance is created if necessary.
 765      *
 766      * @return an AccessibleJTextArea that serves as the
 767      *         AccessibleContext of this JTextArea
 768      */
 769     public AccessibleContext getAccessibleContext() {
 770         if (accessibleContext == null) {
 771             accessibleContext = new AccessibleJTextArea();
 772         }
 773         return accessibleContext;
 774     }
 775 
 776     /**
 777      * This class implements accessibility support for the
 778      * <code>JTextArea</code> class.  It provides an implementation of the
 779      * Java Accessibility API appropriate to text area user-interface
 780      * elements.
 781      * <p>
 782      * <strong>Warning:</strong>
 783      * Serialized objects of this class will not be compatible with
 784      * future Swing releases. The current serialization support is
 785      * appropriate for short term storage or RMI between applications running
 786      * the same version of Swing.  As of 1.4, support for long term storage
 787      * of all JavaBeans&trade;
 788      * has been added to the <code>java.beans</code> package.
 789      * Please see {@link java.beans.XMLEncoder}.
 790      */
 791     @SuppressWarnings("serial") // Same-version serialization only
 792     protected class AccessibleJTextArea extends AccessibleJTextComponent {
 793 
 794         /**
 795          * Gets the state set of this object.
 796          *
 797          * @return an instance of AccessibleStateSet describing the states
 798          * of the object
 799          * @see AccessibleStateSet
 800          */
 801         public AccessibleStateSet getAccessibleStateSet() {
 802             AccessibleStateSet states = super.getAccessibleStateSet();
 803             states.add(AccessibleState.MULTI_LINE);
 804             return states;
 805         }
 806     }
 807 
 808     // --- variables -------------------------------------------------
 809 
 810     private int rows;
 811     private int columns;
 812     private int columnWidth;
 813     private int rowHeight;
 814     private boolean wrap;
 815     private boolean word;
 816 
 817 }