* @return list of lines
*/
private List<AttributedCharacterIterator> splitFormatted(final AttributedString text, final int width) {
final List<AttributedCharacterIterator> lines = new LinkedList<AttributedCharacterIterator>();
final BreakIterator iter = BreakIterator.getLineInstance();
iter.setText(text.getIterator());
int previous = iter.first();
AttributedCharacterIterator best = null;
while (iter.next() != BreakIterator.DONE) {
final AttributedCharacterIterator candidate = text.getIterator(null, previous, iter.current());
if (getPixelWidth(candidate) <= width) {
// check for line breaks within the provided text
// unfortunately, the BreakIterators are too dumb to tell *why* they consider the
// location a break, so the check needs to be implemented here
final CharacterIterator cit = iter.getText();
if (isHardLineBreak(cit)) {
lines.add(candidate);
previous = iter.current();
best = null;
} else {
best = candidate;
}
} else {
if (best == null) {
// could not break the line - the word's simply too long. Use more force to
// to fit it to the width
best = splitAggressively(candidate, width);
// splitAggressively returns an iterator with its own indexing,
// so instead of using it directly we need to adjust the old one
previous += best.getEndIndex() - best.getBeginIndex();
} else {
previous = best.getEndIndex();
// Trim the trailing white space
char endChar = best.last();
int endIndex = previous;
while (Character.isWhitespace(endChar) && endChar != CharacterIterator.DONE) {
endIndex = best.getIndex();
endChar = best.previous();
}
best = text.getIterator(null, best.getBeginIndex(), endIndex);
}
lines.add(best);
// a special check for a hard line break just after the word
// that got moved to the next line
final CharacterIterator cit = iter.getText();
if (isHardLineBreak(cit)) {
lines.add(text.getIterator(null, previous, iter.current()));
previous = iter.current();
}
// Pick the shortest candidate possible (backtrack a bit, if needed)
if (iter.current() > previous + 1) {
iter.previous();
}
best = null;
if (lines.size() > MAX_LINES) {
/*
* Limit the height of the text boxes. Append ellipsis
* to tell the user to take a look at the chat log.
* The last line is removed twice to avoid the situation
* where the last text line would fit on the space the
* ellipsis occupies.
*/
lines.remove(lines.size() - 1);
lines.remove(lines.size() - 1);
lines.add(new AttributedString("...").getIterator());
return lines;
}
}
}
// add the rest of the text, if there's any
if (previous < iter.last()) {
lines.add(text.getIterator(null, previous, iter.last()));
}
return lines;
}