/** * Copyright (c) 2012-2013 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.statusshout.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; } }