We are implementing a very small messenger like application in Java and are using a JTextPane for our messages panel. Now, we are trying to replace common smileys with .gif
files and we use some example that does the job... except that the icons are center aligned and it's annoying. Is there anyway to make it look inline (left align, or normal)? Some help to resolve this would be appreciated.
Here's the initialisation code :
messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new WrapHTMLEditorKit());
messagePane.setContentType("text/html");
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);
The MessageDocument
class :
public class MessageDocument extends HTMLDocument {
static private String NEW_LINE = System.getProperty("line.separator");
static private SimpleAttributeSet DEFAULT_STYLE = new SimpleAttributeSet();
static private SimpleAttributeSet ANNOUNCE_GLOBAL_STYLE = new SimpleAttributeSet();
static private SimpleAttributeSet MESSAGE_GLOBAL_STYLE = new SimpleAttributeSet();
static private SimpleAttributeSet MESSAGE_PRIVATE_STYLE = new SimpleAttributeSet();
static private SimpleAttributeSet ERROR_STYLE = new SimpleAttributeSet();
static{
StyleConstants.setFontFamily(DEFAULT_STYLE, "Courier New");
StyleConstants.setItalic(DEFAULT_STYLE, true);
StyleConstants.setForeground(DEFAULT_STYLE, Color.black);
StyleConstants.setFontFamily(ANNOUNCE_GLOBAL_STYLE, "Courier New");
StyleConstants.setBold(ANNOUNCE_GLOBAL_STYLE, true);
StyleConstants.setForeground(ANNOUNCE_GLOBAL_STYLE, new Color(0, 100, 0));
StyleConstants.setFontFamily(MESSAGE_GLOBAL_STYLE, "Courier New");
StyleConstants.setForeground(MESSAGE_GLOBAL_STYLE, Color.blue);
StyleConstants.setFontFamily(MESSAGE_PRIVATE_STYLE, "Courier New");
StyleConstants.setForeground(MESSAGE_PRIVATE_STYLE, Color.black);
StyleConstants.setBackground(MESSAGE_PRIVATE_STYLE, new Color(220, 220, 255));
StyleConstants.setFontFamily(ERROR_STYLE, "Courier New");
StyleConstants.setForeground(ERROR_STYLE, Color.yellow);
StyleConstants.setBackground(ERROR_STYLE, Color.red);
}
public MessageDocument() {
super();
}
public void addMessage(ServerMessage msg) {
SimpleAttributeSet style;
if (MessageType.ANNOUNCE_GLOBAL.equals(msg.getType()) || MessageType.CONNECTION_EVENT.equals(msg.getType()) || MessageType.DISCONNECTION_EVENT.equals(msg.getType())) {
style = ANNOUNCE_GLOBAL_STYLE;
} else if (MessageType.MESSAGE_GLOBAL.equals(msg.getType())) {
style = MESSAGE_GLOBAL_STYLE;
} else if (MessageType.MESSAGE_PRIVATE.equals(msg.getType())) {
style = MESSAGE_PRIVATE_STYLE;
} else if (MessageType.ERROR.equals(msg.getType())) {
style = ERROR_STYLE;
} else {
style = DEFAULT_STYLE;
}
try {
insertString(getLength(), msg.getMessage() + NEW_LINE, style);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
The WrapHTMLEditorKit
class :
public class WrapHTMLEditorKit extends HTMLEditorKit {
@Override
public ViewFactory getViewFactory() {
return new HTMLFactory() {
public View create(Element e) {
View v = super.create(e);
if (v instanceof InlineView) {
return new InlineView(e) {
public int getBreakWeight(int axis, float pos, float len) {
return GoodBreakWeight;
}
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 == getEndOffset()) {
return this;
}
return createFragment(p0, p1);
}
return this;
}
};
} else if (v instanceof ParagraphView) {
return new ParagraphView(e) {
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
if (r == null) {
r = new SizeRequirements();
}
float pref = layoutPool.getPreferredSpan(axis);
float min = layoutPool.getMinimumSpan(axis);
// Don't include insets, Box.getXXXSpan will include them.
r.minimum = (int) min;
r.preferred = Math.max(r.minimum, (int) pref);
r.maximum = Integer.MAX_VALUE;
r.alignment = 0.5f;
return r;
}
};
}
return v;
}
};
}
}
And finally, the SmileyDocumentListener
class :
public class SmileyDocumentListener implements DocumentListener {
private JTextComponent owner;
private HashMap<String,ImageIcon> smileMap;
public SmileyDocumentListener(JTextComponent owner) {
this.owner = owner;
this.smileMap = new HashMap<String, ImageIcon>();
this.smileMap.put(":)", new ImageIcon("resources/images/smileys/smile.gif"));
}
@Override
public void insertUpdate(DocumentEvent event) {
final DocumentEvent e = event;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (e.getDocument() instanceof StyledDocument) {
try {
StyledDocument doc=(StyledDocument)e.getDocument();
int start = Utilities.getRowStart(owner, Math.max(0, e.getOffset() - 1));
int end = Utilities.getWordStart(owner, e.getOffset() + e.getLength());
String text = doc.getText(start, end-start);
for (String token : smileMap.keySet()) {
int i = text.indexOf(token);
while (i >= 0) {
final SimpleAttributeSet attrs = new SimpleAttributeSet(doc.getCharacterElement(start + i).getAttributes());
if (StyleConstants.getIcon(attrs) == null) {
StyleConstants.setIcon(attrs, smileMap.get(token));
doc.remove(start + i, 2);
doc.insertString(start + i, token, attrs);
}
i = text.indexOf(token, i+token.length());
}
}
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
}
});
}
@Override
public void removeUpdate(DocumentEvent e) {}
@Override
public void changedUpdate(DocumentEvent e) {}
}
Note : that I've tried changing r.alignment = 0.5f;
to 0.0f
in WrapHTMLEditorKit.java
Alright, so I don't know why the icons are shown that way in a
JTextPane
using a content type oftext/html
(and I tried using aJLabel
displaying an icon, setting HTML directly, etc.) Bottom line is that I thought HTML was necessary.I replaced
by
I removed
WrapHTMLEditorKit
and modified the init code withand icons displayed normally.
The contents wraps without a special class and I can still use
SimpleAttributeSet
objects.