TextWrapper.java
/*
* Copyright © 2012 Nokia Corporation. All rights reserved.
* Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
* Oracle and Java are trademarks or registered trademarks of Oracle and/or its
* affiliates. Other product and company names mentioned herein may be trademarks
* or trade names of their respective owners.
* See LICENSE.TXT for license information.
*/
package com.nokia.example.amaze.ui;
import javax.microedition.lcdui.Font;
/**
* Utility class for text word wrapping with Graphics.drawString().
*/
public class TextWrapper {
// Constants
private char LINE_BREAK = '\n';
private char WHITE_SPACE = ' ';
// Members
static private TextWrapper _instance = null;
private String _text = null;
private Font _font;
private int _fontHeight;
private int _totalLength = 0;
private int _width = 0;
private int _start = 0;
private int _position = 0;
/**
* Getters for the singleton instance.
* @param width The width of the text area.
*/
static public TextWrapper instance(final Font font, final int width) {
if (_instance == null) {
_instance = new TextWrapper(font, width);
}
else {
_instance.setFont(font);
_instance.setWidth(width);
}
return _instance;
}
/**
* Constructor.
* @param width The width of the text area.
*/
private TextWrapper(Font font, int width) {
if (font == null || width <= 0) {
throw new IllegalArgumentException();
}
setFont(font);
setWidth(width);
}
/**
* Sets the font. Used for determine the text size.
* @param font The font to set.
*/
public void setFont(final Font font) {
if (font != null && _font != font) {
_font = font;
_fontHeight = _font.getHeight();
}
}
/**
* Sets the width of the text area so that this wrapper knows where to cut
* the line.
* @param width The width of the text area.
*/
public void setWidth(final int width) {
if (width > 0) {
_width = width;
}
}
/**
* @param text The text to wrap.
*/
public void setText(String text) {
if (text == null) {
throw new IllegalArgumentException();
}
_text = text;
_totalLength = _text.length();
_start = 0;
_position = 0;
}
/**
* @return The text set currently.
*/
public String text() {
return _text;
}
/**
* @return The length of the given line.
*/
public final int lineWidth(final String line) {
return _font.stringWidth(line);
}
/**
* @return The height of a single text line.
*/
public final int lineHeight() {
return _fontHeight;
}
/**
* @return The number of lines the current text will be divided to.
*/
public int lineCount() {
if (_text == null && _totalLength == 0) {
return 0;
}
String original = _text;
int count = 0;
while (hasMoreLines()) {
nextLine();
count++;
}
setText(original); // Reset
return count;
}
/**
* @return True if there are still one or more lines left to paint, false
* otherwise.
*/
public boolean hasMoreLines() {
return (_position < (_totalLength - 1));
}
/**
* @return The next line to paint.
*/
public String nextLine() {
int next = nextPosition();
if (_start >= _totalLength || next > _totalLength) {
return null;
}
String retval = _text.substring(_start, next);
_start = next;
if ((_totalLength - 1 > _start)
&& ((_text.charAt(_start) == LINE_BREAK)
|| (_text.charAt(_start) == WHITE_SPACE)))
{
_position++;
_start++;
}
return retval.trim();
}
/**
* @return The next position where to cut the string (i.e. end position of
* the next line).
*/
private int nextPosition() {
int i = nextWordIndex(_position);
int lastBreak = -1;
String line = _text.substring(_position, i);
int lineWidth = _font.stringWidth(line);
while (i < _totalLength && lineWidth <= _width) {
if (_text.charAt(i) == WHITE_SPACE) {
lastBreak = i;
}
else if (_text.charAt(i) == LINE_BREAK) {
lastBreak = i;
break;
}
if (++i < _totalLength) {
i = nextWordIndex(i);
line = _text.substring(_position, i);
lineWidth = _font.stringWidth(line);
}
}
if (i == _totalLength && lineWidth <= _width) {
_position = i;
}
else if (lastBreak == _position) {
_position++;
}
else if (lastBreak < _position) {
_position = i;
}
else {
_position = lastBreak;
}
return _position;
}
/**
* Resolves the position (index) of the next word from the given
* position (startIndex).
* @param startIndex The position where to start looking for the next
* starting word.
* @return The index of the next starting word.
*/
private int nextWordIndex(int startIndex) {
int space = _text.indexOf(WHITE_SPACE, startIndex);
int newLine = _text.indexOf(LINE_BREAK, startIndex);
if (space == -1) {
space = _totalLength;
}
if (newLine == -1) {
newLine = _totalLength;
}
if (space < newLine) {
return space;
}
return newLine;
}
}