Monday, March 10, 2008

Smileys in text component

This must have been done a hundred time before, but let me show you this JTextPane which allow the insertion of smiley in the text :



This is done with a styled document which use a document filter which will apply style on the fly when you insert text.

The code :


package org.clbproduction.cloud.core.gui.components;

import java.util.Enumeration;

import javax.swing.Icon;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.DocumentFilter;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

public class SmileyStyledDocument extends DefaultStyledDocument {

private static final String SMILEY_STYLE_PREFIX = "smiley_";

private JTextPane textPane;


public SmileyStyledDocument(JTextPane textPane) {
this.textPane = textPane;
this.setDocumentFilter(new MessageDocumentFilter());
}

public Style getSmileyStyle(String text) {
Icon icon = SmileyManager.getSmileyIcon(text); // load your icon here
if (icon != null) {
Style style = addStyle(SMILEY_STYLE_PREFIX + text, StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE));
StyleConstants.setAlignment(style, StyleConstants.ALIGN_CENTER);
StyleConstants.setIcon(style, icon);
return style;

} else
return StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
}


public class MessageDocumentFilter extends DocumentFilter {

@Override
public void remove(FilterBypass fb, int offs, int len) throws BadLocationException {
super.remove(fb, offs, len);
this.computeText(fb);
}

@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet a) throws BadLocationException {
super.replace(fb, offset, length, text, a);
this.computeText(fb);
}

@Override
public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
super.insertString(fb, offset, string, attr);
this.computeText(fb);
}

public void computeText(FilterBypass fb) throws BadLocationException {
int caretPos = textPane.getCaretPosition();
for (int i = 0; i < getLength(); i++) {
fb.replace(i, 1, getText(i, 1), StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE););
Enumeration smileys = SmileyManager.getAllSmileys();
while (smileys.hasMoreElements()) {
String smiley = (String) smileys.nextElement();
if (getText(i, smiley.length()).equals(smiley)) {
fb.replace(i, smiley.length(), smiley, getSmileyStyle(smiley));
i = i + smiley.length() - 1;
break;
}
}
}
textPane.setCaretPosition(caretPos);
}

}

}


And how to use it :


JTextPane textPane = new JTextPane();
this.textPane.setStyledDocument(new SmileyStyledDocument(textPane));


I left the implementation of the "SmileyManager" class as an exercice to the reader :)

import java.util.Enumeration;

import javax.swing.Icon;
import javax.swing.ImageIcon;

public class SmileyManager {

public void addSmiley(String text, String icon) {
//...
}

public Icon getSmileyIcon(String text) {
//...
}

public void removeSmiley(String text) {
//...
}

public Enumeration getAllSmileys() {
//...
}

}


Work well with this smiley menu :)

Issues :
- if a smiley is the prefix of an other smiley then only the first one is displayed (for example :| and :|| )
- a new style is added each time a smiley is inserted. We could/should reuse the previously created style

0 comments:

Post a Comment