home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / JTextArea.java < prev    next >
Text File  |  1998-02-26  |  20KB  |  642 lines

  1. /*
  2.  * @(#)JTextArea.java    1.41 98/02/12
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. import java.awt.*;
  23. import com.sun.java.swing.text.*;
  24. import com.sun.java.swing.plaf.*;
  25. import com.sun.java.accessibility.*;
  26.  
  27. /**
  28.  * A TextArea is a multi-line area that displays plain text. 
  29.  * It is intended to be a lightweight component that provides source 
  30.  * compatibility with the java.awt.TextArea class where it can
  31.  * reasonably do so.  This component has capabilities not found in 
  32.  * the java.awt.TextArea class.  The superclass should be consulted for 
  33.  * additional capabilities.  Alternative multi-line text classes with
  34.  * more capabilitites are JTextPane and JEditorPane.
  35.  * <p>
  36.  * The java.awt.TextArea internally handles scrolling.  JTextArea
  37.  * is different in that it doesn't manage scrolling, but implements
  38.  * the swing Scrollable interface.  This allows it to be placed 
  39.  * inside a JScrollPane if scrolling behavior is desired, and used
  40.  * directly if scrolling is not desired.
  41.  * <p>
  42.  * The java.awt.TextArea has the ability to do line wrapping. 
  43.  * This was controlled by the horizontal scrolling policy.  Since
  44.  * scrolling is not done by JTextArea directly, backward 
  45.  * compatibility must be provided another way.  JTextArea has
  46.  * a bound property for line wrapping that controls whether or
  47.  * not it will wrap lines.
  48.  * <p>
  49.  * The java.awt.TextArea could be monitored for changes by adding
  50.  * a TextListener for TextEvent's.  In the JTextComponent based
  51.  * components, changes are broadcasted from the model via a
  52.  * DocumentEvent to DocumentListeners.  The DocumentEvent gives 
  53.  * the location of the change and the kind of change if desired.
  54.  * The code fragment might look something like:
  55.  * <pre><code>
  56.  *    DocumentListener myListener = ??;
  57.  *    JTextArea myArea = ??;
  58.  *    myArea.getDocument().addDocumentListener(myListener);
  59.  * </code></pre>
  60.  * <p>
  61.  * Warning: serialized objects of this class will not be compatible with
  62.  * future swing releases.  The current serialization support is appropriate
  63.  * for short term storage or RMI between Swing1.0 applications.  It will
  64.  * not be possible to load serialized Swing1.0 objects with future releases
  65.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  66.  * baseline for the serialized form of Swing objects.
  67.  *
  68.  * @beaninfo
  69.  *   attribute: isContainer false
  70.  * @author  Timothy Prinzing
  71.  * @version 1.41 02/12/98
  72.  * @see JTextPane
  73.  * @see JEditorPane
  74.  */
  75. public class JTextArea extends JTextComponent {
  76.  
  77.     /**
  78.      * Constructs a new TextArea.
  79.      */
  80.     public JTextArea() {
  81.     this(null, null, 0, 0);
  82.     }
  83.  
  84.     /**
  85.      * Constructs a new TextArea with the specified text displayed.
  86.      *
  87.      * @param text the text to be displayed 
  88.      */
  89.     public JTextArea(String text) {
  90.     this(null, text, 0, 0);
  91.     }
  92.  
  93.     /**
  94.      * Constructs a new empty TextArea with the specified number of
  95.      * rows and columns.
  96.      *
  97.      * @param rows the number of rows
  98.      * @param columns the number of columns
  99.      */
  100.     public JTextArea(int rows, int columns) {
  101.     this(null, null, rows, columns);
  102.     }
  103.  
  104.     /**
  105.      * Constructs a new TextArea with the specified text and number
  106.      * of rows and columns.
  107.      *
  108.      * @param text the text to be displayed
  109.      * @param rows the number of rows
  110.      * @param columns the number of columns
  111.      */
  112.     public JTextArea(String text, int rows, int columns) {
  113.         this(null, text, rows, columns);
  114.     }
  115.  
  116.     /**
  117.      * Constructs a new JTextArea with the given document model, and defaults
  118.      * for all of the other arguments.
  119.      *
  120.      * @param doc  the model to use
  121.      */
  122.     public JTextArea(Document doc) {
  123.     this(doc, null, 0, 0);
  124.     }
  125.  
  126.     /**
  127.      * Constructs a new JTextArea with the specified number of rows
  128.      * and columns, and the given model.  All of the constructors
  129.      * feed through this constructor.
  130.      *
  131.      * @param doc the model to use
  132.      * @param text the text to be displayed
  133.      * @param rows the number of rows
  134.      * @param columns the number of columns
  135.      */
  136.     public JTextArea(Document doc, String text, int rows, int columns) {
  137.     super();
  138.     setName(base + nameCounter++);
  139.     this.rows = rows;
  140.     this.columns = columns;
  141.     if (doc == null) {
  142.         doc = createDefaultModel();
  143.     }
  144.     setDocument(doc);
  145.     if (text != null) {
  146.         setText(text);
  147.     }
  148.     }
  149.  
  150.     /**
  151.      * Returns the class ID for the UI.
  152.      *
  153.      * @return the ID
  154.      * @see JComponent#getUIClassID
  155.      * @see UIDefaults#getUI
  156.      */
  157.     public String getUIClassID() {
  158.     return "TextAreaUI";
  159.     }
  160.  
  161.  
  162.     /**
  163.      * Creates the default implementation of the model
  164.      * to be used at construction if one isn't explicitly 
  165.      * given.
  166.      *
  167.      * @return the default document model
  168.      */
  169.     protected Document createDefaultModel() {
  170.     return new PlainDocument();
  171.     }
  172.  
  173.     /**
  174.      * Sets the number of characters to expand tabs to.
  175.      * This will be multiplied by the maximum advance for
  176.      * variable width fonts.
  177.      *
  178.      * @param size number of characters to expand to
  179.      * @see #getTabSize
  180.      * @beaninfo
  181.      *   preferred: true
  182.      *       bound: true
  183.      * description: the number of characters to expand tabs to
  184.      */
  185.     public void setTabSize(int size) {
  186.     Document doc = getDocument();
  187.     if (doc != null) {
  188.         int old = getTabSize();
  189.         doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(size));
  190.         firePropertyChange("TabSize", old, size);
  191.     }
  192.     }
  193.  
  194.     /**
  195.      * Gets the number of characters used to expand tabs.
  196.      *
  197.      * @return the number of characters
  198.      */
  199.     public int getTabSize() {
  200.     int size = 8;
  201.     Document doc = getDocument();
  202.     if (doc != null) {
  203.         Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute);
  204.         if (i != null) {
  205.         size = i.intValue();
  206.         }
  207.     }
  208.     return size;
  209.     }
  210.  
  211.     /**
  212.      * Set the line-wrapping policy of the text area.  If set
  213.      * to true the lines will be wrapped if they are too long
  214.      * to fit within the allocated width.  If set to false,
  215.      * the lines will always be unwrapped.
  216.      *
  217.      * @param wrap indicates if lines should be wrapped.
  218.      * @see #getLineWrap
  219.      * @beaninfo
  220.      *   preferred: true
  221.      *       bound: true
  222.      * description: should lines be wrapped
  223.      */
  224.     public void setLineWrap(boolean wrap) {
  225.     boolean old = this.wrap;
  226.     this.wrap = wrap;
  227.     firePropertyChange("LineWrap", old, wrap);
  228.     }
  229.  
  230.     /**
  231.      * Get the line-wrapping policy of the text area.  If set
  232.      * to true the lines will be wrapped if they are too long
  233.      * to fit within the allocated width.  If set to false,
  234.      * the lines will always be unwrapped.
  235.      *
  236.      * @returns if lines will be wrapped.
  237.      */
  238.     public boolean getLineWrap() {
  239.     return wrap;
  240.     }
  241.  
  242.     /**
  243.      * Translate an offset into the components text to a 
  244.      * line number.
  245.      * @exception BadLocationException Thrown if the offset is
  246.      * less than zero or greater than the document length.
  247.      */
  248.     public int getLineOfOffset(int offset) throws BadLocationException {
  249.     Document doc = getDocument();
  250.     if (offset < 0) {
  251.         throw new BadLocationException("Can't translate offset to line", -1);
  252.     } else if (offset > doc.getLength()) {
  253.         throw new BadLocationException("Can't translate offset to line", doc.getLength()+1);
  254.     } else {
  255.         Element map = getDocument().getDefaultRootElement();
  256.         return map.getElementIndex(offset);
  257.     }
  258.     }
  259.  
  260.     /**
  261.      * Determine the number of lines contained.
  262.      */
  263.     public int getLineCount() {
  264.     Element map = getDocument().getDefaultRootElement();
  265.     return map.getElementCount();
  266.     }
  267.  
  268.     /**
  269.      * Determine the offset of the start of the given line.
  270.      * @param line  The line number to translate.  Lines are
  271.      * numbered from 0.
  272.      * @exception BadLocationException Thrown if the line is
  273.      * less than zero or greater or equal to the number of
  274.      * lines contained in the document (as reported by 
  275.      * getLineCount).
  276.      */
  277.     public int getLineStartOffset(int line) throws BadLocationException {
  278.     Element map = getDocument().getDefaultRootElement();
  279.     if (line < 0) {
  280.         throw new BadLocationException("Negative line", -1);
  281.     } else if (line >= map.getElementCount()) {
  282.         throw new BadLocationException("No such line", getDocument().getLength()+1);
  283.     } else {
  284.         Element lineElem = map.getElement(line);
  285.         return lineElem.getStartOffset();
  286.     }
  287.     }
  288.  
  289.     /**
  290.      * Determine the offset of the end of the given line.
  291.      * @param line  The line number to translate.  Lines are
  292.      * numbered from 0.
  293.      * @exception BadLocationException Thrown if the line is
  294.      * less than zero or greater or equal to the number of
  295.      * lines contained in the document (as reported by 
  296.      * getLineCount).
  297.      */
  298.     public int getLineEndOffset(int line) throws BadLocationException {
  299.     Element map = getDocument().getDefaultRootElement();
  300.     if (line < 0) {
  301.         throw new BadLocationException("Negative line", -1);
  302.     } else if (line >= map.getElementCount()) {
  303.         throw new BadLocationException("No such line", getDocument().getLength()+1);
  304.     } else {
  305.         Element lineElem = map.getElement(line);
  306.         return lineElem.getEndOffset();
  307.     }
  308.     }
  309.  
  310.     // --- java.awt.TextArea methods ---------------------------------
  311.  
  312.     /**
  313.      * Inserts the specified text at the specified position.
  314.      * <p>
  315.      * This method is thread safe, although most Swing methods
  316.      * are not. Please see 
  317.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  318.      * and Swing</A> for more information.     
  319.      *
  320.      * @param str the text to insert
  321.      * @param pos the position at which to insert
  322.      * @exception IllegalArgumentException  if pos is an
  323.      *  invalid position in the model
  324.      * @see TextComponent#setText
  325.      * @see #replaceRange
  326.      */
  327.     public void insert(String str, int pos) {
  328.     Document doc = getDocument();
  329.     if (doc != null) {
  330.         try {
  331.         doc.insertString(pos, str, null);
  332.         } catch (BadLocationException e) {
  333.         throw new IllegalArgumentException(e.getMessage());
  334.         }
  335.     }
  336.     }
  337.  
  338.     /**
  339.      * Appends the given text to the end of the document.
  340.      * <p>
  341.      * This method is thread safe, although most Swing methods
  342.      * are not. Please see 
  343.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  344.      * and Swing</A> for more information.     
  345.      *
  346.      * @param str the text to insert
  347.      * @see #insert
  348.      */
  349.     public void append(String str) {
  350.     Document doc = getDocument();
  351.     if (doc != null) {
  352.         try {
  353.         doc.insertString(doc.getLength(), str, null);
  354.         } catch (BadLocationException e) {
  355.         }
  356.     }
  357.     }
  358.  
  359.     /**
  360.      * Replaces text from the indicated start to end position with the
  361.      * new text specified.
  362.      * <p>
  363.      * This method is thread safe, although most Swing methods
  364.      * are not. Please see 
  365.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  366.      * and Swing</A> for more information.     
  367.      *
  368.      * @param str the text to use as the replacement
  369.      * @param start the start position
  370.      * @param end the end position
  371.      * @exception IllegalArgumentException  if part of the range is an
  372.      *  invalid position in the model
  373.      * @see #insert
  374.      * @see #replaceRange
  375.      */
  376.     public void replaceRange(String str, int start, int end) {
  377.     if (end < start) {
  378.         throw new IllegalArgumentException("end before start");
  379.     }
  380.     Document doc = getDocument();
  381.     if (doc != null) {
  382.         try {
  383.         doc.remove(start, end - start);
  384.         doc.insertString(start, str, null);
  385.         } catch (BadLocationException e) {
  386.         throw new IllegalArgumentException(e.getMessage());
  387.         }
  388.     }
  389.     }
  390.  
  391.     /**
  392.      * Turns off tab traversal once focus gained.
  393.      *
  394.      * @return true, to indicate that the focus is being managed
  395.      */
  396.     public boolean isManagingFocus() {
  397.         return true;
  398.     }
  399.  
  400.     /**
  401.      * Returns the number of rows in the TextArea.
  402.      *
  403.      * @return the number of rows
  404.      */
  405.     public int getRows() {
  406.     return rows;
  407.     }
  408.  
  409.     /**
  410.      * Sets the number of rows for this TextArea.
  411.      *
  412.      * @param rows the number of rows
  413.      * @exception IllegalArgumentException if rows is less than 0
  414.      * @see #getRows
  415.      * @beaninfo
  416.      * description: the number of rows preferred for display
  417.      */
  418.     public void setRows(int rows) {
  419.     int oldVal = this.rows;
  420.     if (rows < 0) {
  421.         throw new IllegalArgumentException("rows less than zero.");
  422.     }
  423.     if (rows != oldVal) {
  424.         this.rows = rows;
  425.         invalidate();
  426.     }
  427.     }
  428.  
  429.     /**
  430.      * Defines the meaning of the height of a row.  This defaults to
  431.      * the height of the font.
  432.      *
  433.      * @return the height
  434.      */
  435.     protected int getRowHeight() {
  436.     if (rowHeight == 0) {
  437.         FontMetrics metrics = getFontMetrics(getFont());
  438.         rowHeight = metrics.getHeight();
  439.     }
  440.     return rowHeight;
  441.     }
  442.  
  443.     /**
  444.      * Returns the number of columns in the TextArea.
  445.      *
  446.      * @return number of columns
  447.      */
  448.     public int getColumns() {
  449.     return columns;
  450.     }
  451.  
  452.     /**
  453.      * Sets the number of columns for this TextArea.
  454.      *
  455.      * @param columns the number of columns
  456.      * @exception IllegalArgumentException if columns is less than 0
  457.      * @see #getColumns
  458.      * @beaninfo
  459.      * description: the number of columns preferred for display
  460.      */
  461.     public void setColumns(int columns) {
  462.     int oldVal = this.columns;
  463.     if (columns < 0) {
  464.         throw new IllegalArgumentException("columns less than zero.");
  465.     }
  466.     if (columns != oldVal) {
  467.         this.columns = columns;
  468.         invalidate();
  469.     }
  470.     }
  471.  
  472.     /**
  473.      * Gets column width.
  474.      * The meaning of what a column is can be considered a fairly weak
  475.      * notion for some fonts.  This method is used to define the width
  476.      * of a column.  By default this is defined to be the width of the
  477.      * character <em>m</em> for the font used.  This method can be 
  478.      * redefined to be some alternative amount.
  479.      *
  480.      * @return the column width
  481.      */
  482.     protected int getColumnWidth() {
  483.     if (columnWidth == 0) {
  484.         FontMetrics metrics = getFontMetrics(getFont());
  485.         columnWidth = metrics.charWidth('m');
  486.     }
  487.     return columnWidth;
  488.     }
  489.  
  490.  
  491.     /**
  492.      * Returns the minimum size Dimensions of the TextArea.
  493.      * By default this is set to the preferred size.
  494.      *
  495.      * @return the dimensions
  496.      */
  497.     public Dimension getMinimumSize() {
  498.     if ((columns != 0) || (rows != 0)) {
  499.         return getPreferredSize();
  500.     }
  501.     return super.getMinimumSize();
  502.     }
  503.  
  504.     /**
  505.      * Sets the current font.  This removes cached row height and column
  506.      * width so the new font will be reflected.
  507.      *
  508.      * @param f the font to use as the current font
  509.      */
  510.     public void setFont(Font f) {
  511.     super.setFont(f);
  512.     rowHeight = 0; 
  513.     columnWidth = 0;
  514.     revalidate();
  515.     }
  516.  
  517.     /**
  518.      * Returns the String of parameters for this TextArea.
  519.      *
  520.      * @return the string of parameters
  521.      */
  522.     protected String paramString() {
  523.     return super.paramString() + ",rows=" + rows + 
  524.         ",columns=" + columns;
  525.     }
  526.  
  527.     // --- Scrollable methods ----------------------------------------
  528.  
  529.     /**
  530.      * Returns true if a viewport should always force the width of this 
  531.      * Scrollable to match the width of the viewport.  This is implemented
  532.      * to return true if the line wrapping policy is true, and false
  533.      * if lines are not being wrapped.
  534.      * 
  535.      * @return True if a viewport should force the Scrollables width to match its own.
  536.      */
  537.     public boolean getScrollableTracksViewportWidth() {
  538.     return (wrap) ? true : false;
  539.     }
  540.  
  541.     /**
  542.      * Returns the preferred size of the viewport if this component
  543.      * is embedded in a JScrollPane.  This uses the desired column
  544.      * and row settings if they have been set, otherwise the superclass
  545.      * behavior is used.
  546.      * 
  547.      * @return The preferredSize of a JViewport whose view is this Scrollable.
  548.      * @see JViewport#getPreferredSize
  549.      */
  550.     public Dimension getPreferredScrollableViewportSize() {
  551.     Dimension size = super.getPreferredScrollableViewportSize();
  552.     size = (size == null) ? new Dimension(400,400) : size;
  553.     size.width = (columns == 0) ? size.width : columns * getColumnWidth();
  554.     size.height = (rows == 0) ? size.height : rows * getRowHeight();
  555.     return size;
  556.     }
  557.  
  558.     /**
  559.      * Components that display logical rows or columns should compute
  560.      * the scroll increment that will completely expose one new row
  561.      * or column, depending on the value of orientation.  This is implemented
  562.      * to use the vaules returned by the <code>getRowHeight</code> and
  563.      * <code>getColumnWidth</code> methods.
  564.      * <p>
  565.      * Scrolling containers, like JScrollPane, will use this method
  566.      * each time the user requests a unit scroll.
  567.      * 
  568.      * @param visibleRect The view area visible within the viewport
  569.      * @param orientation Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL.
  570.      * @param direction Less than zero to scroll up/left, greater than zero for down/right.
  571.      * @return The "unit" increment for scrolling in the specified direction
  572.      * @see JScrollBar#setUnitIncrement
  573.      * @see #getRowHeight
  574.      * @see #getColumnWidth
  575.      */
  576.     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
  577.     switch (orientation) {
  578.     case SwingConstants.VERTICAL:
  579.         return getRowHeight();
  580.     case SwingConstants.HORIZONTAL:
  581.         return getColumnWidth();
  582.     default:
  583.         throw new IllegalArgumentException("Invalid orientation: " + orientation);
  584.     }
  585.     }
  586.  
  587. /////////////////
  588. // Accessibility support
  589. ////////////////
  590.  
  591.  
  592.     /**
  593.      * Get the AccessibleContext associated with this JTextArea
  594.      *
  595.      * @return the AccessibleContext of this JTextArea
  596.      */
  597.     public AccessibleContext getAccessibleContext() {
  598.         if (accessibleContext == null) {
  599.             accessibleContext = new AccessibleJTextArea();
  600.         }
  601.         return accessibleContext;
  602.     }
  603.  
  604.     /**
  605.      * The class used to obtain the accessible role for this object.
  606.      * <p>
  607.      * Warning: serialized objects of this class will not be compatible with
  608.      * future swing releases.  The current serialization support is appropriate
  609.      * for short term storage or RMI between Swing1.0 applications.  It will
  610.      * not be possible to load serialized Swing1.0 objects with future releases
  611.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  612.      * baseline for the serialized form of Swing objects.
  613.      */
  614.     protected class AccessibleJTextArea extends AccessibleJTextComponent {
  615.  
  616.         /**
  617.          * Gets the state set of this object.
  618.          *
  619.          * @return an instance of AccessibleStateSet describing the states
  620.          * of the object
  621.          * @see AccessibleStateSet
  622.          */
  623.         public AccessibleStateSet getAccessibleStateSet() {
  624.         AccessibleStateSet states = super.getAccessibleStateSet();
  625.         states.add(AccessibleState.MULTI_LINE);
  626.             return states;
  627.         }
  628.     }
  629.  
  630.     // --- variables -------------------------------------------------
  631.  
  632.     private int    rows;
  633.     private int    columns;
  634.     private int columnWidth;
  635.     private int rowHeight;
  636.     private boolean wrap;
  637.  
  638.     private static final String base = "text";
  639.     private static int nameCounter = 0;
  640.  
  641. }
  642.