/* ****************************************************************** * 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); } }