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