001/* TextArea.java -- A multi-line text entry component
002   Copyright (C) 1999, 2004 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package java.awt;
040
041import java.awt.event.KeyEvent;
042import java.awt.peer.ComponentPeer;
043import java.awt.peer.TextAreaPeer;
044import java.util.HashSet;
045import java.util.Set;
046
047import javax.accessibility.AccessibleContext;
048import javax.accessibility.AccessibleStateSet;
049
050
051/**
052 * A TextArea is a text component capable of displaying multiple lines
053 * of user-editable text.  A TextArea handles its own scrolling and
054 * can display vertical and horizontal scrollbars as navigation aids.
055 *
056 * @author Aaron M. Renn (arenn@urbanophile.com)
057 */
058public class TextArea extends TextComponent implements java.io.Serializable
059{
060  /**
061   * Display both horiztonal and vertical scroll bars.
062   */
063  public static final int SCROLLBARS_BOTH = 0;
064
065  /**
066   * Display vertical scroll bar only.
067   */
068  public static final int SCROLLBARS_VERTICAL_ONLY = 1;
069
070  /**
071   * Display horizatonal scroll bar only.
072   */
073  public static final int SCROLLBARS_HORIZONTAL_ONLY = 2;
074
075  /**
076   * Do not display scrollbars.
077   */
078  public static final int SCROLLBARS_NONE = 3;
079
080  /**
081   * Serialization constant.
082   */
083  private static final long serialVersionUID = 3692302836626095722L;
084
085  /**
086   * @serial The number of columns used in this text area's preferred
087   * and minimum size calculations.
088   */
089  private int columns;
090
091  /**
092   * @serial The number of rows used in this text area's preferred and
093   * minimum size calculations.
094   */
095  private int rows;
096
097  /**
098   * @serial The scrollbar display policy.  One of SCROLLBARS_BOTH,
099   * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY,
100   * SCROLLBARS_NONE.
101   */
102  private int scrollbarVisibility;
103
104  /*
105   * The number used to generate the name returned by getName.
106   */
107  private static transient long next_text_number;
108
109  /**
110   * Initialize a new instance of <code>TextArea</code> that is empty.
111   * Conceptually the <code>TextArea</code> has 0 rows and 0 columns
112   * but its initial bounds are defined by its peer or by the
113   * container in which it is packed.  Both horizontal and vertical
114   * scrollbars will be displayed.
115   *
116   * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true
117   */
118  public TextArea ()
119  {
120    this ("", 0, 0, SCROLLBARS_BOTH);
121  }
122
123  /**
124   * Initialize a new instance of <code>TextArea</code> that contains
125   * the specified text.  Conceptually the <code>TextArea</code> has 0
126   * rows and 0 columns but its initial bounds are defined by its peer
127   * or by the container in which it is packed.  Both horizontal and
128   * veritcal scrollbars will be displayed.  The TextArea initially contains
129   * the specified text.  If text specified as <code>null<code>, it will
130   * be set to "".
131   *
132   * @param text The text to display in this text area (<code>null</code> permitted).
133   *
134   * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true
135   */
136  public TextArea (String text)
137  {
138    this (text, 0, 0, SCROLLBARS_BOTH);
139  }
140
141  /**
142   * Initialize a new instance of <code>TextArea</code> that is empty
143   * and can display the specified number of rows and columns of text,
144   * without the need to scroll.  Both horizontal and vertical
145   * scrollbars will be displayed.
146   *
147   * @param rows The number of rows in this text area.
148   * @param columns The number of columns in this text area.
149   *
150   * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true
151   */
152  public TextArea (int rows, int columns)
153  {
154    this ("", rows, columns, SCROLLBARS_BOTH);
155  }
156
157  /**
158   * Initialize a new instance of <code>TextArea</code> that can
159   * display the specified number of rows and columns of text, without
160   * the need to scroll.  The TextArea initially contains the
161   * specified text.  If text specified as <code>null<code>, it will
162   * be set to "".
163   *
164   * @param text The text to display in this text area (<code>null</code> permitted).
165   * @param rows The number of rows in this text area.
166   * @param columns The number of columns in this text area.
167   *
168   * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true
169   */
170  public TextArea (String text, int rows, int columns)
171  {
172    this (text, rows, columns, SCROLLBARS_BOTH);
173  }
174
175  /**
176   * Initialize a new instance of <code>TextArea</code> that initially
177   * contains the specified text.  The TextArea can display the
178   * specified number of rows and columns of text, without the need to
179   * scroll.  This constructor allows specification of the scroll bar
180   * display policy.  The TextArea initially contains the specified text.  
181   * If text specified as <code>null<code>, it will be set to "". 
182   *
183   * @param text The text to display in this text area (<code>null</code> permitted).
184   * @param rows The number of rows in this text area.
185   * @param columns The number of columns in this text area.
186   * @param scrollbarVisibility The scroll bar display policy. One of
187   * SCROLLBARS_BOTH, SCROLLBARS_VERTICAL_ONLY,
188   * SCROLLBARS_HORIZONTAL_ONLY, SCROLLBARS_NONE.
189   *
190   * @exception HeadlessException if GraphicsEnvironment.isHeadless () is true
191   */
192  public TextArea (String text, int rows, int columns, int scrollbarVisibility)
193  {
194    super (text);
195
196    if (GraphicsEnvironment.isHeadless ())
197      throw new HeadlessException ();
198
199    if (rows < 0)
200      this.rows = 0;
201    else
202      this.rows = rows;
203    
204    if (columns < 0)
205      this.columns = 0;
206    else
207      this.columns = columns;
208
209    if (scrollbarVisibility < 0 || scrollbarVisibility > 4)
210      this.scrollbarVisibility = SCROLLBARS_BOTH;
211    else
212      this.scrollbarVisibility = scrollbarVisibility;
213
214    // TextAreas need to receive tab key events so we override the
215    // default forward and backward traversal key sets.
216    Set s = new HashSet ();
217    s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB,
218                                         KeyEvent.CTRL_DOWN_MASK));
219    setFocusTraversalKeys (KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, s);
220    s = new HashSet ();
221    s.add (AWTKeyStroke.getAWTKeyStroke (KeyEvent.VK_TAB,
222                                         KeyEvent.SHIFT_DOWN_MASK
223                                         | KeyEvent.CTRL_DOWN_MASK));
224    setFocusTraversalKeys (KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, s);
225  }
226
227  /**
228   * Retrieve the number of columns that this text area would prefer
229   * to display.  This value may or may not correspond to the number
230   * of columns that are actually displayed.
231   *
232   * @return The preferred number of columns.
233   */
234  public int getColumns ()
235  {
236    return columns;
237  }
238
239  /**
240   * Set the preferred number of columns for this text area.  This
241   * method does not cause the number of columns displayed by the text
242   * area to be updated, if the text area is currently visible.
243   *
244   * @param columns The preferred number of columns.
245   *
246   * @exception IllegalArgumentException If columns is less than zero.
247   */
248  public synchronized void setColumns (int columns)
249  {
250    if (columns < 0)
251      throw new IllegalArgumentException ("Value is less than zero: "
252                                          + columns);
253
254    this.columns = columns;
255  }
256
257  /**
258   * Retrieve the number of rows that this text area would prefer to
259   * display.  This value may or may not correspond to the number of
260   * rows that are actually displayed.
261   *
262   * @return The preferred number of rows.
263   */
264  public int getRows ()
265  {
266    return rows;
267  }
268
269  /**
270   * Set the preferred number of rows for this text area.  This method
271   * does not cause the number of columns displayed by the text area
272   * to be updated, if the text area is currently visible.
273   *
274   * @param rows The preferred number of rows.
275   *
276   * @exception IllegalArgumentException If rows is less than zero.
277   */
278  public synchronized void setRows (int rows)
279  {
280    if (rows < 1)
281      throw new IllegalArgumentException ("Value is less than one: " + rows);
282
283    this.rows = rows;
284  }
285
286  /**
287   * Retrieve the minimum size for this text area.
288   *
289   * @return The minimum size for this text field.
290   */
291  public Dimension getMinimumSize ()
292  {
293    return getMinimumSize (getRows (), getColumns ());
294  }
295
296  /**
297   * Retrieve the minimum size for this text area.  If the minimum
298   * size has been set, then rows and columns are used in the calculation.
299   *
300   * @param rows The number of rows to use in the minimum size
301   * calculation.
302   * @param columns The number of columns to use in the minimum size
303   * calculation.
304   *
305   * @return The minimum size for this text area.
306   */
307  public Dimension getMinimumSize (int rows, int columns)
308  {
309    return minimumSize (rows, columns);
310  }
311
312  /**
313   * Retrieve the minimum size for this text area.
314   * 
315   * @return The minimum size for this text area.
316   *
317   * @deprecated This method is deprecated in favor of
318   * <code>getMinimumSize ()</code>.
319   */
320  public Dimension minimumSize ()
321  {
322    return minimumSize (getRows (), getColumns ());
323  }
324
325  /**
326   * Retrieve the minimum size for this text area.  If the minimum
327   * size has been set, then rows and columns are used in the calculation.
328   *
329   * @param rows The number of rows to use in the minimum size
330   * calculation.
331   * @param columns The number of columns to use in the minimum size
332   * calculation.
333   *
334   * @return The minimum size for this text area.
335   *
336   * @deprecated This method is deprecated in favor of
337   * <code>getMinimumSize (int, int)</code>.
338   */
339  public Dimension minimumSize (int rows, int columns)
340  {
341    if (isMinimumSizeSet())
342      return new Dimension(minSize);
343    
344    TextAreaPeer peer = (TextAreaPeer) getPeer ();
345    if (peer == null)
346      return new Dimension (getWidth(), getHeight());
347
348    return peer.getMinimumSize (rows, columns);
349  }
350
351  /**
352   * Retrieve the preferred size for this text area.
353   *
354   * @return The preferred size for this text field.
355   */
356  public Dimension getPreferredSize ()
357  {
358    return getPreferredSize (getRows (), getColumns ());
359  }
360
361  /**
362   * Retrieve the preferred size for this text area.  If the preferred
363   * size has been set, then rows and columns are used in the calculation.
364   *
365   * @param rows The number of rows to use in the preferred size
366   * calculation.
367   * @param columns The number of columns to use in the preferred size
368   * calculation.
369   *
370   * @return The preferred size for this text area.
371   */
372  public Dimension getPreferredSize (int rows, int columns)
373  {
374    return preferredSize (rows, columns);
375  }
376
377  /**
378   * Retrieve the preferred size for this text area.
379   *
380   * @return The preferred size for this text field.
381   *
382   * @deprecated This method is deprecated in favor of
383   * <code>getPreferredSize ()</code>.
384   */
385  public Dimension preferredSize ()
386  {
387    return preferredSize (getRows (), getColumns ());
388  }
389
390  /**
391   * Retrieve the preferred size for this text area.  If the preferred
392   * size has been set, then rows and columns are used in the calculation.
393   *
394   * @param rows The number of rows to use in the preferred size
395   * calculation.
396   * @param columns The number of columns to use in the preferred size
397   * calculation.
398   *
399   * @return The preferred size for this text area.
400   *
401   * @deprecated This method is deprecated in favor of
402   * <code>getPreferredSize (int, int)</code>.
403   */
404  public Dimension preferredSize (int rows, int columns)
405  {
406    if (isPreferredSizeSet())
407      return new Dimension(prefSize);
408    
409    TextAreaPeer peer = (TextAreaPeer) getPeer ();
410    if (peer == null)
411      return new Dimension (getWidth(), getHeight());
412
413    return peer.getPreferredSize (rows, columns);
414  }
415
416  /**
417   * Retrieve the scroll bar display policy -- one of SCROLLBARS_BOTH,
418   * SCROLLBARS_VERTICAL_ONLY, SCROLLBARS_HORIZONTAL_ONLY,
419   * SCROLLBARS_NONE.
420   *
421   * @return The current scroll bar display policy.
422   */
423  public int getScrollbarVisibility ()
424  {
425    return scrollbarVisibility;
426  }
427
428  /**
429   * Notify this object that it should create its native peer.
430   */
431  public void addNotify ()
432  {
433    if (getPeer () == null)
434      setPeer ((ComponentPeer) getToolkit().createTextArea (this));
435  }
436
437  /**
438   * Append the specified text to the end of the current text.
439   *
440   * @param str The text to append.
441   */
442  public void append (String str)
443  {
444    appendText (str);
445  }
446
447  /**
448   * Append the specified text to the end of the current text.
449   *
450   * @param str The text to append.
451   *
452   * @deprecated This method is deprecated in favor of
453   * <code>append ()</code>.
454   */
455  public void appendText (String str)
456  {
457    TextAreaPeer peer = (TextAreaPeer) getPeer ();
458
459    if (peer != null)
460      peer.insert (str, peer.getText().length ());
461    else
462      setText(getText() + str);   
463  }
464
465  /**
466   * Insert the specified text at the specified position.  The first
467   * character in the text area is at position zero.
468   *
469   * @param str The text to insert.
470   * @param pos The position at which to insert text.
471   */
472  public void insert (String str, int pos)
473  {
474    insertText (str, pos);
475  }
476
477  /**
478   * Insert the specified text at the specified position.  The first
479   * character in the text area is at position zero.
480   *
481   * @param str The text to insert.
482   * @param pos The position at which to insert text.
483   *
484   * @deprecated This method is deprecated in favor of
485   * <code>insert ()</code>.
486   */
487  public void insertText (String str, int pos)
488  {
489    String tmp1 = null;
490    String tmp2 = null;
491    
492    TextAreaPeer peer = (TextAreaPeer) getPeer ();
493
494    if (peer != null)
495      peer.insert (str, pos);
496    else
497      {
498        tmp1 = getText().substring(0, pos);
499        tmp2 = getText().substring(pos, getText().length());
500        setText(tmp1 + str + tmp2);
501      }
502  }
503
504  /**
505   * Replace a range of characters with the specified text.  The
506   * character at the start position will be replaced, unless start ==
507   * end.  The character at the end posistion will not be replaced.
508   * The first character in the text area is at position zero.  The
509   * length of the replacement text may differ from the length of the
510   * text that is replaced.
511   *
512   * @param str The new text for the range.
513   * @param start The start position of the replacement range.
514   * @param end The end position of the replacement range.
515   */
516  public void replaceRange (String str, int start, int end)
517  {
518    replaceText (str, start, end);
519  }
520
521  /**
522   * Replace a range of characters with the specified text.  The
523   * character at the start position will be replaced, unless start ==
524   * end.  The character at the end posistion will not be replaced.
525   * The first character in the text area is at position zero.  The
526   * length of the replacement text may differ from the length of the
527   * text that is replaced.
528   *
529   * @param str The new text for the range.
530   * @param start The start position of the replacement range.
531   * @param end The end position of the replacement range.
532   *
533   * @deprecated This method is deprecated in favor of
534   * <code>replaceRange ()</code>.
535   */
536  public void replaceText (String str, int start, int end)
537  {
538    String tmp1 = null;
539    String tmp2 = null;
540
541    TextAreaPeer peer = (TextAreaPeer) getPeer();
542
543    if (peer != null)
544      peer.replaceRange(str, start, end);
545    else
546      {
547        tmp1 = getText().substring(0, start);
548        tmp2 = getText().substring(end, getText().length());
549        setText(tmp1 + str + tmp2);
550      }
551  }
552
553  /**
554   * Retrieve a debugging string for this text area.
555   *
556   * @return A debugging string for this text area.
557   */
558  protected String paramString ()
559  {
560    String sbVisibility = "";
561
562    switch (scrollbarVisibility)
563      {
564      case SCROLLBARS_BOTH:
565        sbVisibility = "both";
566        break;
567      case SCROLLBARS_VERTICAL_ONLY:
568        sbVisibility = "vertical-only";
569        break;
570      case SCROLLBARS_HORIZONTAL_ONLY:
571        sbVisibility = "horizontal-only";
572        break;
573      case SCROLLBARS_NONE:
574        sbVisibility = "none";
575        break;
576      }
577
578    String editable = "";
579    if (isEditable ())
580      editable = "editable,";
581
582    return getName () + "," + getX () + "," + getY () + "," + getWidth ()
583           + "x" + getHeight () + "," + "text=" + getText () + "," + editable
584           + "selection=" + getSelectionStart () + "-" + getSelectionEnd ()
585           + ",rows=" + rows + ",columns=" + columns + ",scrollbarVisibility="
586           + sbVisibility;
587  }
588
589  /**
590   * Generate a unique name for this text area.
591   *
592   * @return A unique name for this text area.
593   */
594  String generateName ()
595  {
596    return "text" + getUniqueLong ();
597  }
598
599  private static synchronized long getUniqueLong ()
600  {
601    return next_text_number++;
602  }
603  
604  protected class AccessibleAWTTextArea extends AccessibleAWTTextComponent
605  {
606    private static final long serialVersionUID = 3472827823632144419L;
607
608    protected AccessibleAWTTextArea()
609    {
610    }
611    
612    public AccessibleStateSet getAccessibleStateSet()
613    {
614      return super.getAccessibleStateSet();
615    }
616  }
617  
618  /**
619   * Gets the AccessibleContext associated with this <code>TextArea</code>.
620   * The context is created, if necessary.
621   *
622   * @return the associated context
623   */
624  public AccessibleContext getAccessibleContext()
625  {
626    /* Create the context if this is the first request */
627    if (accessibleContext == null)
628      accessibleContext = new AccessibleAWTTextArea();
629    return accessibleContext;
630  }
631}