I put a JTextPane into a JScrollPane. As I tried, the JTextPane will auto-wrap a long line if it exceed the width of the display area. And the auto wrapping is based on word boundary, such as a space character.
My content contains a lot of space. And I want to display it literally. So I need auto-wrapping, but I want it happen ONLY at the maximum width of display area, NOT on word boundary.
How?
What I have tried:
- Replace all the space with '\0', so literally, my content is a
single big word.
ADD 1
Below is my failed attempt after reading StanislavL's solution. I am not blaming his solution since my scenario is not exactly the same as his.
StanislavL's solution requires that a row contains at least 2 LabelViews. According to him, this is imposed by Swing's implementation of layout() method where forced break works only if row view has more than one child (see: http://java-sl.com/wrap.html). So StanislavL deliberately assigned a special attribute to the \r character which ensure a separate LabelView. And use the \r as a landmark of wrapping. But in my scenario I cannot insert any characters to my content.
My idea is simple, just provide a customized implementation of ViewFactory for the StyledEditorKit since the ViewFactory interface determines the break weight and how a break should happen:
this.jTextPane.setEditorKit(new StyledEditorKit(){
@Override
public ViewFactory getViewFactory(){
return new LetterWrappingStyledViewFactory(maxCharWidth);
}
});
Below is my implementation of the interface ViewFactory:
public class LetterWrappingStyledViewFactory implements ViewFactory {
public int maxCharWidth = -1; // this is the max width where I want wrap to happen.
public LetterWrappingStyledViewFactory(int maxCharWidth) {
this.maxCharWidth = maxCharWidth;
}
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new LabelView(elem) {
public int getBreakWeight(int axis, float pos, float len) {
if (axis == View.X_AXIS) {
checkPainter();
int p0 = getStartOffset();
int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
if (p1 > maxCharWidth)
return View.ForcedBreakWeight;
else
return View.BadBreakWeight;
}
return super.getBreakWeight(axis, pos, len);
}
public View breakView(int axis, int p0, float pos, float len) {
if (axis == View.X_AXIS) {
checkPainter();
int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
if (p0 == getStartOffset() && p1 <= maxCharWidth) {
return this;
}
return createFragment(p0, maxCharWidth);
}
return this;
}
};
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
return new ParagraphView(elem);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
return new BoxView(elem, View.Y_AXIS);
} else if (kind.equals(StyleConstants.ComponentElementName)) {
return new ComponentView(elem);
} else if (kind.equals(StyleConstants.IconElementName)) {
return new IconView(elem);
}
}
// default to text display
return new LabelView(elem);
}
}