Merge from mainline
[official-gcc.git] / libjava / classpath / javax / swing / JTextArea.java
blob9b50febe3617bac4164e36ac107d3112867da696
1 /* JTextArea.java --
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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. */
39 package javax.swing;
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;
54 /**
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.
59 * <p>
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.
71 * <p>
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
80 * view port.
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
96 /**
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()
109 super();
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.
121 return state;
126 * Compatible with Sun's JDK
128 private static final long serialVersionUID = -6141680179310439825L;
131 * The number of rows used by the component.
133 private int rows;
136 * The number of columns used by the component.
138 private int columns;
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.
155 public JTextArea()
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);
220 // Only explicitly setText() when there is actual text since
221 // setText() might be overridden and not expected to be called
222 // from the constructor (as in JEdit).
223 if (text != null)
224 setText(text);
225 setRows(rows);
226 setColumns(columns);
230 * Appends the supplied text to the current contents
231 * of the document model.
233 * @param toAppend the text to append
235 public void append(String toAppend)
239 getDocument().insertString(getText().length(), toAppend, null);
241 catch (BadLocationException exception)
243 /* This shouldn't happen in theory -- but, if it does... */
244 throw new RuntimeException("Unexpected exception occurred.", exception);
246 if (toAppend != null && toAppend.length() > 0)
247 revalidate();
251 * Creates the default document model.
253 * @return a new default model
255 protected Document createDefaultModel()
257 return new PlainDocument();
261 * Returns true if the width of this component should be forced
262 * to match the width of a surrounding view port. When line wrapping
263 * is turned on, this method returns true.
265 * @return true if lines are wrapped.
267 public boolean getScrollableTracksViewportWidth()
269 return lineWrap ? true : super.getScrollableTracksViewportWidth();
273 * Returns the increment that is needed to expose exactly one new line
274 * of text. This is implemented here to return the values of
275 * {@link #getRowHeight} and {@link #getColumnWidth}, depending on
276 * the value of the argument <code>direction</code>.
278 * @param visibleRect the view area that is visible in the viewport
279 * @param orientation either {@link SwingConstants#VERTICAL} or
280 * {@link SwingConstants#HORIZONTAL}
281 * @param direction less than zero for up/left scrolling, greater
282 * than zero for down/right scrolling
284 * @return the increment that is needed to expose exactly one new row
285 * or column of text
287 * @throws IllegalArgumentException if <code>orientation</code> is invalid
289 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
290 int direction)
292 if (orientation == SwingConstants.VERTICAL)
293 return getRowHeight();
294 else if (orientation == SwingConstants.HORIZONTAL)
295 return getColumnWidth();
296 else
297 throw new IllegalArgumentException("orientation must be either "
298 + "javax.swing.SwingConstants.VERTICAL "
299 + "or "
300 + "javax.swing.SwingConstants.HORIZONTAL"
305 * Returns the preferred size of that text component in the case
306 * it is embedded within a JScrollPane. This uses the column and
307 * row settings if they are explicitly set, or fall back to
308 * the superclass's behaviour.
310 * @return the preferred size of that text component in the case
311 * it is embedded within a JScrollPane
313 public Dimension getPreferredScrollableViewportSize()
315 if ((rows > 0) && (columns > 0))
316 return new Dimension(columns * getColumnWidth(), rows * getRowHeight());
317 else
318 return super.getPreferredScrollableViewportSize();
322 * Returns the UI class ID string.
324 * @return the string "TextAreaUI"
326 public String getUIClassID()
328 return "TextAreaUI";
332 * Returns the current number of columns.
334 * @return number of columns
336 public int getColumns()
338 return columns;
342 * Sets the number of rows.
344 * @param columns number of columns
346 * @exception IllegalArgumentException if columns is negative
348 public void setColumns(int columns)
350 if (columns < 0)
351 throw new IllegalArgumentException();
353 if (columns != this.columns)
355 this.columns = columns;
356 revalidate();
361 * Returns the current number of rows.
363 * @return number of rows
365 public int getRows()
367 return rows;
371 * Sets the number of rows.
373 * @param rows number of rows
375 * @exception IllegalArgumentException if rows is negative
377 public void setRows(int rows)
379 if (rows < 0)
380 throw new IllegalArgumentException();
382 if (rows != this.rows)
384 this.rows = rows;
385 revalidate();
390 * Checks whether line wrapping is enabled.
392 * @return <code>true</code> if line wrapping is enabled,
393 * <code>false</code> otherwise
395 public boolean getLineWrap()
397 return lineWrap;
401 * Enables/disables line wrapping.
403 * @param flag <code>true</code> to enable line wrapping,
404 * <code>false</code> otherwise
406 public void setLineWrap(boolean flag)
408 if (lineWrap == flag)
409 return;
411 boolean oldValue = lineWrap;
412 lineWrap = flag;
413 firePropertyChange("lineWrap", oldValue, lineWrap);
417 * Checks whether word style wrapping is enabled.
419 * @return <code>true</code> if word style wrapping is enabled,
420 * <code>false</code> otherwise
422 public boolean getWrapStyleWord()
424 return wrapStyleWord;
428 * Enables/Disables word style wrapping.
430 * @param flag <code>true</code> to enable word style wrapping,
431 * <code>false</code> otherwise
433 public void setWrapStyleWord(boolean flag)
435 if (wrapStyleWord == flag)
436 return;
438 boolean oldValue = wrapStyleWord;
439 wrapStyleWord = flag;
440 firePropertyChange("wrapStyleWord", oldValue, wrapStyleWord);
444 * Returns the number of characters used for a tab.
445 * This defaults to 8.
447 * @return the current number of spaces used for a tab.
449 public int getTabSize()
451 return tabSize;
455 * Sets the number of characters used for a tab to the
456 * supplied value. If a change to the tab size property
457 * occurs (i.e. newSize != tabSize), a property change event
458 * is fired.
460 * @param newSize The new number of characters to use for a tab.
462 public void setTabSize(int newSize)
464 if (tabSize == newSize)
465 return;
467 int oldValue = tabSize;
468 tabSize = newSize;
469 firePropertyChange("tabSize", oldValue, tabSize);
472 protected int getColumnWidth()
474 FontMetrics metrics = getToolkit().getFontMetrics(getFont());
475 return metrics.charWidth('m');
478 public int getLineCount()
480 return getDocument().getDefaultRootElement().getElementCount();
483 public int getLineStartOffset(int line)
484 throws BadLocationException
486 int lineCount = getLineCount();
488 if (line < 0 || line > lineCount)
489 throw new BadLocationException("Non-existing line number", line);
491 Element lineElem = getDocument().getDefaultRootElement().getElement(line);
492 return lineElem.getStartOffset();
495 public int getLineEndOffset(int line)
496 throws BadLocationException
498 int lineCount = getLineCount();
500 if (line < 0 || line > lineCount)
501 throw new BadLocationException("Non-existing line number", line);
503 Element lineElem = getDocument().getDefaultRootElement().getElement(line);
504 return lineElem.getEndOffset();
507 public int getLineOfOffset(int offset)
508 throws BadLocationException
510 Document doc = getDocument();
512 if (offset < doc.getStartPosition().getOffset()
513 || offset >= doc.getEndPosition().getOffset())
514 throw new BadLocationException("offset outside of document", offset);
516 return doc.getDefaultRootElement().getElementIndex(offset);
519 protected int getRowHeight()
521 FontMetrics metrics = getToolkit().getFontMetrics(getFont());
522 return metrics.getHeight();
526 * Inserts the supplied text at the specified position. Nothing
527 * happens in the case that the model or the supplied string is null
528 * or of zero length.
530 * @param string The string of text to insert.
531 * @param position The position at which to insert the supplied text.
532 * @throws IllegalArgumentException if the position is &lt; 0 or greater
533 * than the length of the current text.
535 public void insert(String string, int position)
537 // Retrieve the document model.
538 Document doc = getDocument();
540 // Check the model and string for validity.
541 if (doc == null
542 || string == null
543 || string.length() == 0)
544 return;
546 // Insert the text into the model.
549 doc.insertString(position, string, null);
551 catch (BadLocationException e)
553 throw new IllegalArgumentException("The supplied position, "
554 + position + ", was invalid.");
558 public void replaceRange(String text, int start, int end)
560 Document doc = getDocument();
562 if (start > end
563 || start < doc.getStartPosition().getOffset()
564 || end >= doc.getEndPosition().getOffset())
565 throw new IllegalArgumentException();
569 doc.remove(start, end - start);
570 doc.insertString(start, text, null);
572 catch (BadLocationException e)
574 // This cannot happen as we check offset above.
579 * Returns the preferred size for the JTextArea. This is the maximum of
580 * the size that is needed to display the content and the requested size
581 * as per {@link #getColumns} and {@link #getRows}.
583 * @return the preferred size of the JTextArea
585 public Dimension getPreferredSize()
587 int reqWidth = getColumns() * getColumnWidth();
588 int reqHeight = getRows() * getRowHeight();
589 View view = getUI().getRootView(this);
590 int neededWidth = (int) view.getPreferredSpan(View.HORIZONTAL);
591 int neededHeight = (int) view.getPreferredSpan(View.VERTICAL);
592 return new Dimension(Math.max(reqWidth, neededWidth),
593 Math.max(reqHeight, neededHeight));
597 * Returns the accessible context associated with the <code>JTextArea</code>.
599 * @return the accessible context associated with the <code>JTextArea</code>
601 public AccessibleContext getAccessibleContext()
603 if (accessibleContext == null)
604 accessibleContext = new AccessibleJTextArea();
605 return accessibleContext;