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.
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);
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();
}
}
}
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;
}
};
}
}
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 of text/html
(and I tried using a JLabel
displaying an icon, setting HTML directly, etc.) Bottom line is that I thought HTML was necessary.
I replaced
public class MessageDocument extends HTMLDocument {
by
public class MessageDocument extends StyledDocument {
I removed WrapHTMLEditorKit
and modified the init code with
messagePane = new JTextPane();
messagePane.setEditable(false);
messagePane.setEditorKit(new StyledEditorKit());
MessageDocument doc = new MessageDocument();
doc.addDocumentListener(new SmileyDocumentListener(messagePane));
messagePane.setStyledDocument(doc);
and icons displayed normally.
The contents wraps without a special class and I can still use SimpleAttributeSet
objects.