2 Copyright (C) 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java
.awt
.Dimension
;
42 import java
.awt
.FontMetrics
;
43 import java
.awt
.Rectangle
;
45 import javax
.accessibility
.AccessibleContext
;
46 import javax
.accessibility
.AccessibleStateSet
;
47 import javax
.swing
.text
.BadLocationException
;
48 import javax
.swing
.text
.Document
;
49 import javax
.swing
.text
.Element
;
50 import javax
.swing
.text
.JTextComponent
;
51 import javax
.swing
.text
.PlainDocument
;
52 import javax
.swing
.text
.View
;
55 * The <code>JTextArea</code> component provides a multi-line area for displaying
56 * and editing plain text. The component is designed to act as a lightweight
57 * replacement for the heavyweight <code>java.awt.TextArea</code> component,
58 * which provides similar functionality using native widgets.
61 * This component has additional functionality to the AWT class. It follows
62 * the same design pattern as seen in other text components, such as
63 * <code>JTextField</code>, <code>JTextPane</code> and <code>JEditorPane</code>,
64 * and embodied in <code>JTextComponent</code>. These classes separate the text
65 * (the model) from its appearance within the onscreen component (the view). The
66 * text is held within a <code>javax.swing.text.Document</code> object, which can
67 * also maintain relevant style information where necessary. As a result, it is the
68 * document that should be monitored for textual changes, via
69 * <code>DocumentEvent</code>s delivered to registered
70 * <code>DocumentListener</code>s, rather than this component.
73 * Unlike <code>java.awt.TextArea</code>, <code>JTextArea</code> does not
74 * handle scrolling. Instead, this functionality is delegated to a
75 * <code>JScrollPane</code>, which can contain the text area and handle
76 * scrolling when required. Likewise, the word wrapping functionality
77 * of the AWT component is converted to a property of this component
78 * and the <code>rows</code> and <code>columns</code> properties
79 * are used in calculating the preferred size of the scroll pane's
82 * @author Michael Koch (konqueror@gmx.de)
83 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
84 * @see java.awt.TextArea
85 * @see javax.swing.text.JTextComponent
86 * @see javax.swing.JTextField
87 * @see javax.swing.JTextPane
88 * @see javax.swing.JEditorPane
89 * @see javax.swing.text.Document
90 * @see javax.swing.event.DocumentEvent
91 * @see javax.swing.event.DocumentListener
94 public class JTextArea
extends JTextComponent
97 * Provides accessibility support for <code>JTextArea</code>.
99 * @author Roman Kennke (kennke@aicas.com)
101 protected class AccessibleJTextArea
extends AccessibleJTextComponent
105 * Creates a new <code>AccessibleJTextArea</code> object.
107 protected AccessibleJTextArea()
113 * Returns the accessible state of this <code>AccessibleJTextArea</code>.
115 * @return the accessible state of this <code>AccessibleJTextArea</code>
117 public AccessibleStateSet
getAccessibleStateSet()
119 AccessibleStateSet state
= super.getAccessibleStateSet();
120 // TODO: Figure out what state must be added here to the super's state.
126 * Compatible with Sun's JDK
128 private static final long serialVersionUID
= -6141680179310439825L;
131 * The number of rows used by the component.
136 * The number of columns used by the component.
141 * Whether line wrapping is enabled or not.
143 private boolean lineWrap
;
146 * The number of characters equal to a tab within the text.
148 private int tabSize
= 8;
150 private boolean wrapStyleWord
;
153 * Creates a new <code>JTextArea</code> object.
157 this(null, null, 0, 0);
161 * Creates a new <code>JTextArea</code> object.
163 * @param text the initial text
165 public JTextArea(String text
)
167 this(null, text
, 0, 0);
171 * Creates a new <code>JTextArea</code> object.
173 * @param rows the number of rows
174 * @param columns the number of cols
176 * @exception IllegalArgumentException if rows or columns are negative
178 public JTextArea(int rows
, int columns
)
180 this(null, null, rows
, columns
);
184 * Creates a new <code>JTextArea</code> object.
186 * @param text the initial text
187 * @param rows the number of rows
188 * @param columns the number of cols
190 * @exception IllegalArgumentException if rows or columns are negative
192 public JTextArea(String text
, int rows
, int columns
)
194 this(null, text
, rows
, columns
);
198 * Creates a new <code>JTextArea</code> object.
200 * @param doc the document model to use
202 public JTextArea(Document doc
)
204 this(doc
, null, 0, 0);
208 * Creates a new <code>JTextArea</code> object.
210 * @param doc the document model to use
211 * @param text the initial text
212 * @param rows the number of rows
213 * @param columns the number of cols
215 * @exception IllegalArgumentException if rows or columns are negative
217 public JTextArea(Document doc
, String text
, int rows
, int columns
)
219 setDocument(doc
== null ?
createDefaultModel() : doc
);
226 * Appends the supplied text to the current contents
227 * of the document model.
229 * @param toAppend the text to append
231 public void append(String toAppend
)
235 getDocument().insertString(getText().length(), toAppend
, null);
237 catch (BadLocationException exception
)
239 /* This shouldn't happen in theory -- but, if it does... */
240 throw new RuntimeException("Unexpected exception occurred.", exception
);
242 if (toAppend
!= null && toAppend
.length() > 0)
247 * Creates the default document model.
249 * @return a new default model
251 protected Document
createDefaultModel()
253 return new PlainDocument();
257 * Returns true if the width of this component should be forced
258 * to match the width of a surrounding view port. When line wrapping
259 * is turned on, this method returns true.
261 * @return true if lines are wrapped.
263 public boolean getScrollableTracksViewportWidth()
265 return lineWrap ?
true : super.getScrollableTracksViewportWidth();
269 * Returns the increment that is needed to expose exactly one new line
270 * of text. This is implemented here to return the values of
271 * {@link #getRowHeight} and {@link #getColumnWidth}, depending on
272 * the value of the argument <code>direction</code>.
274 * @param visibleRect the view area that is visible in the viewport
275 * @param orientation either {@link SwingConstants#VERTICAL} or
276 * {@link SwingConstants#HORIZONTAL}
277 * @param direction less than zero for up/left scrolling, greater
278 * than zero for down/right scrolling
280 * @return the increment that is needed to expose exactly one new row
283 * @throws IllegalArgumentException if <code>orientation</code> is invalid
285 public int getScrollableUnitIncrement(Rectangle visibleRect
, int orientation
,
288 if (orientation
== SwingConstants
.VERTICAL
)
289 return getRowHeight();
290 else if (orientation
== SwingConstants
.HORIZONTAL
)
291 return getColumnWidth();
293 throw new IllegalArgumentException("orientation must be either "
294 + "javax.swing.SwingConstants.VERTICAL "
296 + "javax.swing.SwingConstants.HORIZONTAL"
301 * Returns the preferred size of that text component in the case
302 * it is embedded within a JScrollPane. This uses the column and
303 * row settings if they are explicitly set, or fall back to
304 * the superclass's behaviour.
306 * @return the preferred size of that text component in the case
307 * it is embedded within a JScrollPane
309 public Dimension
getPreferredScrollableViewportSize()
311 if ((rows
> 0) && (columns
> 0))
312 return new Dimension(columns
* getColumnWidth(), rows
* getRowHeight());
314 return super.getPreferredScrollableViewportSize();
318 * Returns the UI class ID string.
320 * @return the string "TextAreaUI"
322 public String
getUIClassID()
328 * Returns the current number of columns.
330 * @return number of columns
332 public int getColumns()
338 * Sets the number of rows.
340 * @param columns number of columns
342 * @exception IllegalArgumentException if columns is negative
344 public void setColumns(int columns
)
347 throw new IllegalArgumentException();
349 if (columns
!= this.columns
)
351 this.columns
= columns
;
357 * Returns the current number of rows.
359 * @return number of rows
367 * Sets the number of rows.
369 * @param rows number of rows
371 * @exception IllegalArgumentException if rows is negative
373 public void setRows(int rows
)
376 throw new IllegalArgumentException();
378 if (rows
!= this.rows
)
386 * Checks whether line wrapping is enabled.
388 * @return <code>true</code> if line wrapping is enabled,
389 * <code>false</code> otherwise
391 public boolean getLineWrap()
397 * Enables/disables line wrapping.
399 * @param flag <code>true</code> to enable line wrapping,
400 * <code>false</code> otherwise
402 public void setLineWrap(boolean flag
)
404 if (lineWrap
== flag
)
407 boolean oldValue
= lineWrap
;
409 firePropertyChange("lineWrap", oldValue
, lineWrap
);
413 * Checks whether word style wrapping is enabled.
415 * @return <code>true</code> if word style wrapping is enabled,
416 * <code>false</code> otherwise
418 public boolean getWrapStyleWord()
420 return wrapStyleWord
;
424 * Enables/Disables word style wrapping.
426 * @param flag <code>true</code> to enable word style wrapping,
427 * <code>false</code> otherwise
429 public void setWrapStyleWord(boolean flag
)
431 if (wrapStyleWord
== flag
)
434 boolean oldValue
= wrapStyleWord
;
435 wrapStyleWord
= flag
;
436 firePropertyChange("wrapStyleWord", oldValue
, wrapStyleWord
);
440 * Returns the number of characters used for a tab.
441 * This defaults to 8.
443 * @return the current number of spaces used for a tab.
445 public int getTabSize()
451 * Sets the number of characters used for a tab to the
452 * supplied value. If a change to the tab size property
453 * occurs (i.e. newSize != tabSize), a property change event
456 * @param newSize The new number of characters to use for a tab.
458 public void setTabSize(int newSize
)
460 if (tabSize
== newSize
)
463 int oldValue
= tabSize
;
465 firePropertyChange("tabSize", oldValue
, tabSize
);
468 protected int getColumnWidth()
470 FontMetrics metrics
= getToolkit().getFontMetrics(getFont());
471 return metrics
.charWidth('m');
474 public int getLineCount()
476 return getDocument().getDefaultRootElement().getElementCount();
479 public int getLineStartOffset(int line
)
480 throws BadLocationException
482 int lineCount
= getLineCount();
484 if (line
< 0 || line
> lineCount
)
485 throw new BadLocationException("Non-existing line number", line
);
487 Element lineElem
= getDocument().getDefaultRootElement().getElement(line
);
488 return lineElem
.getStartOffset();
491 public int getLineEndOffset(int line
)
492 throws BadLocationException
494 int lineCount
= getLineCount();
496 if (line
< 0 || line
> lineCount
)
497 throw new BadLocationException("Non-existing line number", line
);
499 Element lineElem
= getDocument().getDefaultRootElement().getElement(line
);
500 return lineElem
.getEndOffset();
503 public int getLineOfOffset(int offset
)
504 throws BadLocationException
506 Document doc
= getDocument();
508 if (offset
< doc
.getStartPosition().getOffset()
509 || offset
>= doc
.getEndPosition().getOffset())
510 throw new BadLocationException("offset outside of document", offset
);
512 return doc
.getDefaultRootElement().getElementIndex(offset
);
515 protected int getRowHeight()
517 FontMetrics metrics
= getToolkit().getFontMetrics(getFont());
518 return metrics
.getHeight();
522 * Inserts the supplied text at the specified position. Nothing
523 * happens in the case that the model or the supplied string is null
526 * @param string The string of text to insert.
527 * @param position The position at which to insert the supplied text.
528 * @throws IllegalArgumentException if the position is < 0 or greater
529 * than the length of the current text.
531 public void insert(String string
, int position
)
533 // Retrieve the document model.
534 Document doc
= getDocument();
536 // Check the model and string for validity.
539 || string
.length() == 0)
542 // Insert the text into the model.
545 doc
.insertString(position
, string
, null);
547 catch (BadLocationException e
)
549 throw new IllegalArgumentException("The supplied position, "
550 + position
+ ", was invalid.");
554 public void replaceRange(String text
, int start
, int end
)
556 Document doc
= getDocument();
559 || start
< doc
.getStartPosition().getOffset()
560 || end
>= doc
.getEndPosition().getOffset())
561 throw new IllegalArgumentException();
565 doc
.remove(start
, end
- start
);
566 doc
.insertString(start
, text
, null);
568 catch (BadLocationException e
)
570 // This cannot happen as we check offset above.
575 * Returns the preferred size for the JTextArea. This is the maximum of
576 * the size that is needed to display the content and the requested size
577 * as per {@link #getColumns} and {@link #getRows}.
579 * @return the preferred size of the JTextArea
581 public Dimension
getPreferredSize()
583 int reqWidth
= getColumns() * getColumnWidth();
584 int reqHeight
= getRows() * getRowHeight();
585 View view
= getUI().getRootView(this);
586 int neededWidth
= (int) view
.getPreferredSpan(View
.HORIZONTAL
);
587 int neededHeight
= (int) view
.getPreferredSpan(View
.VERTICAL
);
588 return new Dimension(Math
.max(reqWidth
, neededWidth
),
589 Math
.max(reqHeight
, neededHeight
));
593 * Returns the accessible context associated with the <code>JTextArea</code>.
595 * @return the accessible context associated with the <code>JTextArea</code>
597 public AccessibleContext
getAccessibleContext()
599 if (accessibleContext
== null)
600 accessibleContext
= new AccessibleJTextArea();
601 return accessibleContext
;