All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.appdapter.gui.swing.JAutoResizingTextArea Maven / Gradle / Ivy

Go to download

Appdapter Maven project including Java and Scala, produces jar, not bundle. Excludes concrete SLF4J binding.

The newest version!
package org.appdapter.gui.swing;

/*
 * @(#)AutosizingTextArea.java 5/11/2005
 *
 * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
 */

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Insets;

import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.PlainDocument;

/**
 * An extended version of JTextArea that automatically resizes itself vertically. This component works best
 * when used in a layout that obeys preferred height of its components. For example, you can use a
 * BorderLayout and place JAutoResizingTextArea to the north or south side. Similarly, you can
 * use a JideBoxLayout and use FLEXIBLE or FIX as the constraint.
 */
public class JAutoResizingTextArea extends JEditorPane {

	int rows = 24, columns = 80;
	boolean linewrap;

	public void superSetRows(int newRows) {
		rows = newRows;
		//JTextArea super.setRows(newRows);

	}

	public void setLineWrap(boolean wrap) {
		linewrap = wrap;
		//JTextArea super.setLineWrap(wrap);

	}

	public void setColumns(int columns) {
		this.columns = columns;
		//JTextArea  super.setColumns(columns);
	}

	public int getRows() {
		return rows;
		//JTextArea return super.getRows();
	}

	/**
	 * Determines the number of lines contained in the area.
	 *
	 * @return the number of lines > 0
	 */
	public int getLineCount() {
		Element map = getDocument().getDefaultRootElement();
		return map.getElementCount();
	}

	public void replaceRange(String str, int start, int end) {
		if (end < start) {
			throw new IllegalArgumentException("end before start");
		}
		Document doc = getDocument();
		if (doc != null) {
			try {
				if (doc instanceof AbstractDocument) {
					((AbstractDocument) doc).replace(start, end - start, str, null);
				} else {
					doc.remove(start, end - start);
					doc.insertString(start, str, null);
				}
			} catch (BadLocationException e) {
				throw new IllegalArgumentException(e.getMessage());
			}
		}
	}

	public void setTabSize(int size) {
		Document doc = getDocument();
		if (doc != null) {
			int old = getTabSize();
			doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(size));
			firePropertyChange("tabSize", old, size);
		}
	}

	public int getTabSize() {
		int size = 8;
		Document doc = getDocument();
		if (doc != null) {
			Integer i = (Integer) doc.getProperty(PlainDocument.tabSizeAttribute);
			if (i != null) {
				size = i.intValue();
			}
		}
		return size;
	}

	/**
	 * Determines the offset of the start of the given line.
	 *
	 * @param line  the line number to translate >= 0
	 * @return the offset >= 0
	 * @exception BadLocationException thrown if the line is
	 * less than zero or greater or equal to the number of
	 * lines contained in the document (as reported by 
	 * getLineCount).
	 */
	public int getLineStartOffset(int line) throws BadLocationException {
		int lineCount = getLineCount();
		if (line < 0) {
			throw new BadLocationException("Negative line", -1);
		} else if (line >= lineCount) {
			throw new BadLocationException("No such line", getDocument().getLength() + 1);
		} else {
			Element map = getDocument().getDefaultRootElement();
			Element lineElem = map.getElement(line);
			return lineElem.getStartOffset();
		}
	}

	public int getLineOfOffset(int offset) throws BadLocationException {
		Document doc = getDocument();
		if (offset < 0) {
			throw new BadLocationException("Can't translate offset to line", -1);
		} else if (offset > doc.getLength()) {
			throw new BadLocationException("Can't translate offset to line", doc.getLength() + 1);
		} else {
			Element map = getDocument().getDefaultRootElement();
			return map.getElementIndex(offset);
		}
	}

	public void insert(String str, int pos) {
		Document doc = getDocument();
		if (doc != null) {
			try {
				doc.insertString(pos, str, null);
			} catch (BadLocationException e) {
				throw new IllegalArgumentException(e.getMessage());
			}
		}
	}

	public int getRowHeight() {

		FontMetrics metrics = getFontMetrics(getFont());
		return metrics.getHeight();

	}

	/**
	 * Default maximum height of the text area in rows.
	 */
	public static final int DEFAULT_MAX_ROWS = 20;

	/**
	 * Default minimum height of the text area in rows.
	 */
	public static final int DEFAULT_MIN_ROWS = 1;

	private int _maxRows;
	private int _minRows;
	private ResizingDocumentListener _listener;

	/**
	 * Creates a textarea with the default minimum and maximum number of rows.
	 */
	public JAutoResizingTextArea() {
		this(DEFAULT_MIN_ROWS, DEFAULT_MAX_ROWS);
	}

	/**
	 * Creates a textarea with the specified minimum number of rows.
	 *
	 * @param minRows The minimum number of rows that this textarea can have.
	 */
	public JAutoResizingTextArea(int minRows) {
		this(minRows, DEFAULT_MAX_ROWS);
	}

	/**
	 * Creates a textarea with the specified minimum and maximum number of rows.
	 *
	 * @param minRows The minimum number of rows that this textarea can have.
	 * @param maxRows The maximum number of rows that this textarea can have.
	 */
	public JAutoResizingTextArea(int minRows, int maxRows) {
		super();
		setMinRows(minRows);
		setMaxRows(maxRows);
		setRows(minRows);
	}

	/**
	 * Creates a textarea with the default minimum and maximum row count and the provided initial text. The textarea is
	 * sized to fit the provided text.
	 *
	 * @param text The initial text to display.
	 */
	public JAutoResizingTextArea(String text) {
		this();
		setText(text);
	}

	/**
	 * Create a new JAutoResizingTextArea with a height bounded by the provided minimum and maximum row
	 * counts and with its width dictated by the provided column count.
	 *
	 * @param minRows The minimum number of rows that this textarea can have
	 * @param maxRows The maximum number of rows that this textarea can have.
	 * @param columns The number of columns that this textarea has.
	 */
	public JAutoResizingTextArea(int minRows, int maxRows, int columns) {
		this(minRows, maxRows);
		setMinRows(minRows);
		setMaxRows(maxRows);
		setColumns(columns);
	}

	/**
	 * Create a new JAutoResizingTextArea with a height bounded by the provided minimum and maximum row
	 * counts and with its width dictated by the provided column count. The textarea is sized to fit the provided text.
	 *
	 * @param text    The initial text to display in the textarea.
	 * @param minRows The minimum number of rows that this textarea can have
	 * @param maxRows The maximum number of rows that this textarea can have.
	 * @param columns The number of columns that this textarea has.
	 *
	 * @throws IllegalArgumentException if the rows or columns arguments are negative.
	 */
	public JAutoResizingTextArea(String text, int minRows, int maxRows, int columns) {
		this(minRows, maxRows, columns);
		setText(text);
	}

	/**
	 * Create a new JAutoResizingTextArea using a Document. The document will be set to the
	 * text area using {@link #setDocument(javax.swing.text.Document)}.
	 *
	 * @param doc the document.
	 */
	public JAutoResizingTextArea(Document doc) {
		this();
		setDocument(doc);
	}

	/**
	 * Constructs a new JAutoResizingTextArea with the specified number of rows and columns, and the given
	 * model.  All of the constructors feed through this constructor.
	 *
	 * @param doc     the model to use, or create a default one if null
	 * @param text    the text to be displayed, null if none
	 * @param minRows the minimum number of rows >= 0
	 * @param maxRows the maximum number of rows >= 0
	 * @param columns the number of columns >= 0
	 *
	 * @throws IllegalArgumentException if the rows or columns arguments are negative.
	 */

	public JAutoResizingTextArea(Document doc, String text, int minRows, int maxRows, int columns) {
		//super(doc, text, minRows, columns);
		setMaxRows(maxRows);
		setMinRows(minRows);
	}

	/**
	 * Sets the number of visible rows. The row value will be forced to the boundaries of the range [minRows ...
	 * maxRows] if it is outside that range.
	 *
	 * @param rows The number of rows to show
	 */
	public void setRows(int rows) {
		int oldRow = getRows();
		int newRow = clipRowCount(rows);
		superSetRows(newRow);

		numberOfRowsUpdated(oldRow, newRow);
	}

	/**
	 * Called when the number of rows is updated. By default, it will get the parent scroll pane and call revalidate.
	 * Subclass can override it to customize the behavior when number of rows is updated.
	 *
	 * @param oldRow the previous row count.
	 * @param newRow the new row count.
	 */
	protected void numberOfRowsUpdated(int oldRow, int newRow) {
		// look for a parent ScrollPane and revalidate its container
		// otherwise revalidate the text area's container
		JScrollPane scroll = getParentScrollPane();
		if (scroll != null) {
			Container parent = scroll.getParent();
			if (parent != null && parent instanceof JComponent) {
				JComponent component = (JComponent) parent;
				component.revalidate();
			}
		}
	}

	/**
	 * Gets the maximum number of rows that will be displayed. You can set it using {@link #setMaxRows(int)} or passed
	 * in using constructor such as {@link #JAutoResizingTextArea(int, int)}.
	 *
	 * @return the maximum number of rows that will be displayed.
	 */
	public int getMaxRows() {
		return _maxRows;
	}

	/**
	 * Sets the maximum number of rows that will be displayed.
	 *
	 * @param maxRows The maximum number of rows.
	 */
	public void setMaxRows(int maxRows) {
		_maxRows = maxRows;
		setRows(clipRowCount(getRows()));
	}

	/**
	 * Gets the minimum number of rows that will be displayed. You can set it using {@link #setMinRows(int)} or passed
	 * in using constructor such as {@link #JAutoResizingTextArea(int, int)}.
	 *
	 * @return the minimum number of rows that will be displayed.
	 */
	public int getMinRows() {
		return _minRows;
	}

	/**
	 * Sets the minimum number of rows that will be displayed
	 *
	 * @param minRows The minimum number of rows.
	 */
	public void setMinRows(int minRows) {
		_minRows = minRows;
		setRows(clipRowCount(getRows()));
	}

	@Override public void setDocument(Document doc) {
		Document document = getDocument();
		if (document != null && _listener != null) {
			document.removeDocumentListener(_listener);
		}
		super.setDocument(doc);
		if (doc != null) {
			if (_listener == null) {
				_listener = new ResizingDocumentListener();
			}
			doc.addDocumentListener(_listener);
		}

		updateSize();
	}

	/**
	 * Clips the given row count to fall within the range [m_minRows .. m_maxRows] (inclusive).
	 *
	 * @param rows The row count to clip.
	 *
	 * @return a row count clipped to the above range
	 */
	private int clipRowCount(int rows) {
		int r = Math.min(_maxRows, rows); // clip to upper bounds
		r = Math.max(_minRows, r); // clip to lower bounds
		return r;
	}

	/**
	 * Listens to document change events and updates the row count appropriately.
	 */
	private class ResizingDocumentListener implements DocumentListener {
		public void insertUpdate(DocumentEvent e) {
			updateSize();
		}

		public void removeUpdate(DocumentEvent e) {
			updateSize();
		}

		public void changedUpdate(DocumentEvent e) {
			updateSize();
		}

	}

	private int getHeight(int rows) {
		Insets insets = getInsets();
		return rows * getRowHeight() + insets.top + insets.bottom;
	}

	private void updateSize() {
		// look for a parent ScrollPane and revalidate its container
		// otherwise revalidate the text area's container
		Container parent = getParentScrollPane();
		if (parent != null && parent.getParent() instanceof JComponent) {
			JComponent component = (JComponent) parent.getParent();
			component.revalidate();
		}
	}

	@Override public Dimension getPreferredScrollableViewportSize() {
		Dimension size = getPreferredSize();
		if (getMinRows() != 0 && getMaxRows() != 0) {
			size.height = Math.max(getHeight(getMinRows()), Math.min(getHeight(getMaxRows()), size.height));
		}
		return size;
	}

	/**
	 * Gets the parent scroll pane if any.
	 *
	 * @return the parent scroll pane. If not found, null will be returned.
	 */
	private JScrollPane getParentScrollPane() {
		Component parent = getParent();
		if (parent != null && parent instanceof JViewport) {
			return (JScrollPane) parent.getParent();
		}
		return null;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy