001    /* TextArea.java -- A multi-line text entry component
002       Copyright (C) 1999, 2004 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.awt;
040    
041    import java.awt.event.KeyEvent;
042    import java.awt.peer.ComponentPeer;
043    import java.awt.peer.TextAreaPeer;
044    import java.util.HashSet;
045    import java.util.Set;
046    
047    import javax.accessibility.AccessibleContext;
048    import 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     */
058    public 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    }