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