Dans Word, il y a une fonctionnalité qui permet d'afficher les caractères invisibles : espaces insécables, retour chariot, espaces, etc.
J'ai essayé de reproduire cela en java avec le composant JTextPane qui sert de base à tout les éditeurs de texte un peu avancées.
Pour cela il faut sous-classer la classe LabelView qui est chargé du rendu des fragments de textes à l'écran.
L'éditorKit qui permet d'utiliser cette nouvelle classe :
package org.bigcrunch.text;
import java.awt.Container;
import javax.swing.text.AbstractDocument;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.LabelView;
import javax.swing.text.Segment;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
public class ExtendedEditorKit extends StyledEditorKit {
public final static String DISPLAY_INVISIBLE_CHARS = "invisibleChars";
protected static ViewFactory defaultFactory;
/**
* @return the view factory
*/
public ViewFactory getViewFactory() {
if(defaultFactory==null)
defaultFactory = new ExtendedViewFactory(super.getViewFactory());
return defaultFactory;
}
/**
* @return a instance of ExtendedDocument()
*/
@Override
public Document createDefaultDocument() {
return new ExtendedDocument();
}
public static class ExtendedViewFactory implements ViewFactory{
private ViewFactory parentViewFactory;
public ExtendedViewFactory(ViewFactory parentViewFactory){
super();
this.parentViewFactory = parentViewFactory;
}
public View create(Element elem) {
if(AbstractDocument.ContentElementName.equals(elem.getName()))
return new ExtendedLabelView(elem);
return parentViewFactory.create(elem);
}
}
public static class ExtendedLabelView extends LabelView{
public ExtendedLabelView(Element elem) {
super(elem);
}
/**
* Fetch a reference to the text that occupies
* the given range. This is normally used by
* the GlyphPainter to determine what characters
* it should render glyphs for.
*
* @param p0 the starting document offset >= 0
* @param p1 the ending document offset >= p0
* @return the <code>Segment</code> containing the text
*/
public Segment getText(int p0, int p1) {
Segment text = super.getText(p0,p1);
Container c = this.getContainer();
if(c instanceof JTextComponent){
if(Boolean.TRUE.equals(((JTextComponent)c).getClientProperty(DISPLAY_INVISIBLE_CHARS))){
char array[] = new char[text.array.length];
System.arraycopy(text.array,0,array,0,text.array.length);
for(int i = 0;i<text.array.length;i++){
switch(text.array[i]){
case ' ': array[i] = '\u00B7';break;
case '\u00A0': array[i] = '\u00B0';break;
case '\n': array[i] = '\u00B6';break;
}
}
text.array = array;
}
}
return text;
}
}
}
On voit que la méthode getText() de la classe LabelView a été modifié afin de replacer les caractères visibles par des caractères invisibles à l'affichage et seulement à l'affichage.
Cet EditorKit utilise une sous-classe de DefaultStyleDocument qui permet d'ajouter automatiquement des espaces insécables avant les caractères de punctuations (:, ?, !, etc)
/*
* Created on 12 avr. 2006 by JO
*/
package org.bigcrunch.text;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
public class ExtendedDocument extends DefaultStyledDocument {
protected final static char punctuationCharacters[] = {'!','?',':',';',')'};
@Override
public void insertString(int offset,String text,AttributeSet attr) throws BadLocationException{
//if it's a punctuation character, add a insecable space before
if(offset>0&&text.length()==1){
char previousChar = this.getText(offset-1,1).charAt(0);
char currentChar = text.charAt(0);
for(int i = 0;i<punctuationCharacters.length;i++)
if(currentChar==punctuationCharacters[i]&&previousChar==' '){
offset--;
text = "\u00A0"+text;
replace(offset,1,text,attr);
return;
}
}
super.insertString(offset, addInsecableSpaces(text),attr);
}
@Override
public void replace(int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
//if it's a punctuation character, add a insecable space before
if(offset>0&&text.length()==1){
char previousChar = this.getText(offset-1,1).charAt(0);
char currentChar = text.charAt(0);
for(int i = 0;i<punctuationCharacters.length;i++)
if(currentChar==punctuationCharacters[i]&&previousChar==' '){
offset--;
length++;
text = "\u00A0"+text;
}
}
super.replace(offset, length, addInsecableSpaces(text), attrs);
}
public static String addInsecableSpaces(String theText){
char c[] = theText.toCharArray();
for(int i = 1;i<c.length;i++){
if(Character.getType(c[i])==Character.OTHER_PUNCTUATION
&&Character.getType(c[i-1])==Character.SPACE_SEPARATOR
){
c[i-1]='\u00A0';
}
}
return new String(c);
}
}
L'utilisation est très simple et permet de changer de mode d'affichage à n'importe quel moment:
final JTextPane editor = new JTextPane();
editor.setEditorKit(new ExtendedEditorKit());
editor.putClientProperty( ExtendedEditorKit.DISPLAY_INVISIBLE_CHARS, Boolean.TRUE);
La prochaine étape sera d'afficher les tabulations.