/*
******************************************************************
* Copyright (C) David CARSON (carson@wwa.com) 09/1997 *
* *
* Permission is granted to use, copy, modify and distriubte this *
* software for NON-COMMERCIAL use. For commercial use, please *
* contact the author. I'm not liable for damages resulting from *
* the use of this software or its derivates. *
******************************************************************
* Feel free to contact me for help, comments, bugs,contributions,*
* job offers. *
******************************************************************
* e-mail: carson@wwa.com or david@interpro-solutions.com *
* http://shoga.wwa.com/~carson/ *
******************************************************************
WrapTextArea version 1.0.1, 09/1997
Wraps text within a text area to fit in the available width.
03 Oct 97 DAC - fixed bug with constructors calling wrong setEditable()
*/
import java.awt.TextArea;
import java.awt.Font;
import java.awt.FontMetrics;
import java.util.StringTokenizer;
import java.awt.Scrollbar;
/** This WrapTextArea component wraps the text within the text
* area to fit in the available width. It breaks the text on the
* nearest space to the end of the text. If the word is larger
* than the available width, the word is broken.
*
Author: David Carson
*
Release Date: Nov. 1997
*
Version: 1.0.1
* @author David Carson (carson@wwa.com)
* @version 1.0.1
*/
public class WrapTextArea extends TextArea
{
/** The unformated text within the TextArea
*/
protected String originalText_ = null;
/** Scroll bar width
*/
protected int scrollbarWidth = 0;
/* The newline character, platform-dependant.
*/
private String nLine = System.getProperty("line.separator");
private int nLineLength = nLine.length();
/** Creates a new wrapped text area. Sets the text Area
* as read only.
* @see java.awt.TextArea
*/
public WrapTextArea()
{
super();
super.setEditable(false);
}
/** Creates a new wrapped text area. Sets the text Area
* as read only.
* @param rows the number of rows
* @param cols the number of columns
* @see java.awt.TextArea
*/
public WrapTextArea(int rows, int cols)
{
super(rows, cols);
super.setEditable(false);
}
/** Creates a new wrapped text area. Sets the text Area
* as read only.
* @param text the text to be displayed
* @see java.awt.TextArea
*/
public WrapTextArea(String text)
{
super(text);
originalText_ = text;
super.setEditable(false);
// This will get formated when layout is called
// which will occur when the screen is first shown
// or resized.
}
/** Creates a new wrapped text area. Sets the text Area
* as read only.
* @param text the text to be displayed
* @param rows the number of rows
* @param cols the number of columns
* @see java.awt.TextArea
*/
public WrapTextArea(String text, int rows, int cols)
{
super(text, rows, cols);
originalText_ = text;
super.setEditable(false);
// This will get formated when layout is called
// which will occur when the screen is first shown
// or resized.
}
/** Appends the given text to this text area's current text.
* @param text the text to append
* @see java.awt.TextArea#appendText
*/
public void appendText(String text)
{
originalText_ = originalText_ + text;
wrapText();
}
/** Returns the text of the wrap text area.
* @return the text of this text component.
* @see java.awt.TextComponent#getText
*/
public String getText()
{
return originalText_;
}
/** Inserts the specified text at the specified position in this text area.
* If the position specified is less than zero, the text is inserted at the
* beginning. If the position specified is greater than the text length, then
* the specified text is append to the end.
* @param text the text to insert
* @param pos the position at which to insert the text
* @see java.awt.TextArea#insertText
*/
public void insertText(String text, int pos)
{
if (pos >= originalText_.length())
{
appendText(text);
return;
}
if (pos <= 0)
{
originalText_ = text + originalText_;
wrapText();
return;
}
try
{
originalText_ = originalText_.substring(0,pos) + text
+ originalText_.substring(pos);
}
catch (Exception e)
{ // As bounds have already been checked
// an exception should not be thrown
System.out.println("Serious error in WrapTextArea:insertText()");
}
wrapText();
}
/** This method is called to lay out the subcomponents of this component so that
* they fit inside the borders of this component. Most applications should not call
* this method directly.
* @see java.awt.Component#layout
*/
public void layout()
{
super.layout();
// Calculate the scroll bar width once
if (scrollbarWidth == 0)
{
try
{
Scrollbar sb = new Scrollbar(Scrollbar.VERTICAL);
sb.addNotify();
scrollbarWidth = sb.preferredSize().width;
}
catch (Exception e)
{
scrollbarWidth = 32;
}
scrollbarWidth += 4; // to allow for 3D outline lines
}
// More that likely we need to format the text
wrapText();
}
/** Replaces the text in the text area from the start (inclusive) index
* to the end (exclusive) index with the new text specified. If either
* of the positions are outside the string length, the text is left
* unchanged.
* @param text the replacement text
* @param posStart the start position
* @param posEnd the end position
* @see java.awt.TextArea#replaceText
*/
public void replaceText(String text, int posStart, int posEnd)
{
try
{
originalText_ = originalText_.substring(0,posStart) +
text + originalText_.substring(posEnd);
}
catch (Exception e)
{
System.out.println("Serious error in WrapTextArea:replaceText()");
return;
}
wrapText();
}
/** This class only provides a readonly word wrapped text area. Calling
* this method has no effect.
* @param flag - the flag is ignored
* @see java.awt.TextComponent#setEditable
*/
public void setEditable(boolean flag)
{
;
}
/** Sets the text of this text component to be the specified text.
* @param text the new text to set
* @see java.awt.TextComponent#setText
*/
public void setText(String text)
{
originalText_ = text;
wrapText();
}
/** Performs the text wrapping task. Takes into account the
* current font size and window size.
*/
protected void wrapText()
{
if (originalText_ == null)
return;
String fixedText = new String(originalText_);
// If the font is undetermined,
// then the text can not be displayed
if (getFont() == null)
return;
// Must have some room to work in
int width = size().width;
// allow for a scroll bar.
width -= scrollbarWidth;
if (width <= 0)
return;
FontMetrics fontMetric_ = getFontMetrics(getFont());
// Now the fun part, reformat the text to fit
int widthOfChar = fontMetric_.charWidth('W');
int approxCharsPerLine = width / widthOfChar;
int charPosition = 0;
StringTokenizer st = new StringTokenizer(fixedText);
while (st.hasMoreTokens())
{
String nextToken = st.nextToken();
int tokenLength = nextToken.length();
while (fontMetric_.stringWidth(nextToken) > width)
{
int breakSpace = 0;
int breakPoint = approxCharsPerLine;
// find a break point,
try
{
if (fontMetric_.stringWidth(nextToken.substring(0, breakPoint)) > width)
{
breakPoint--;
while (fontMetric_.stringWidth(nextToken.substring(0, breakPoint)) > width)
{
breakPoint--;
}
}
else
{
breakPoint++;
while (fontMetric_.stringWidth(nextToken.substring(0, breakPoint)) < width)
{
breakPoint++;
}
// bring it back one
breakPoint--;
}
// By this stage we should have found the exact
// position to break the line, now just find a space
breakSpace = breakPoint;
while(nextToken.charAt(breakSpace) != ' ' && breakSpace > 0)
{
breakSpace --;
}
if (breakSpace == 0)
{ // the token is one long word, then just break the word
breakSpace = breakPoint;
fixedText = fixedText.substring(0,breakSpace+charPosition) + nLine +
fixedText.substring(breakSpace+charPosition+nLineLength);
// Account for the extra character
//charPosition++;
charPosition += nLineLength;
}
else
{ // replace the space with a new line character
fixedText = fixedText.substring(0,breakSpace+charPosition) + nLine +
fixedText.substring(breakSpace+charPosition+nLineLength);
}
}
catch (Exception e)
{ // Should never throw
System.out.println("Serious error in WrapTextArea:wrapText():\n" +
"breakPoint: " + breakPoint +
"\nbreakSpace: " + breakSpace +
"\ncharPosition: " + charPosition +
"\nnextToken: " + nextToken +
"\nException Message: " +e.getMessage());
}
nextToken = nextToken.substring(breakSpace+nLineLength);
charPosition += breakSpace+nLineLength;
tokenLength = -1;
}
charPosition += tokenLength + nLineLength; // +1 to allow for the eaten space by tokenizer
}
super.setText(fixedText);
}
}