For a personal project, in need to provide users a simple IDE with syntax highlighting in java swing. I'm using a jTextPane inserted in a jScrollPane as this component can style the text with many colors. As a consequence, I cannot use a jTextArea.
I've chosen to use a custom DocumentFilter to deal with new text and highlight it when needed. However, I've notice that when doing so, typing characters in one line may alter the word wrapping in other lines. This lead to a very weird behavior as several lines are affected by a single character addition.
Here is a MWE that reproduce the problem :
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.mbrebion.jtextpanetest;
import java.awt.Color;
import javax.swing.JTextPane;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
/**
*
* @author mbrebion
*/
public class mainWindow extends javax.swing.JFrame {
/**
* Creates new form mainWindow
*/
public mainWindow() {
initComponents();
CustomDocumentFilter cdf = new CustomDocumentFilter(jTextPane1);
((AbstractDocument) jTextPane1.getDocument()).setDocumentFilter(cdf);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTextPane1 = new javax.swing.JTextPane();
jButton1 = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jScrollPane1.setViewportView(jTextPane1);
jButton1.setText("reStyle");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(55, Short.MAX_VALUE)
.addComponent(jButton1)
.addContainerGap(56, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 323, Short.MAX_VALUE)
.addGap(2, 2, 2)
.addComponent(jButton1))
);
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(mainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new mainWindow().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextPane jTextPane1;
// End of variables declaration
}
class CustomDocumentFilter extends DocumentFilter {
protected AttributeSet blackAttributeSet;
protected StyledDocument styledDocument;
protected JTextPane jtp;
public CustomDocumentFilter(JTextPane jtp) {
this.jtp = jtp;
styledDocument = jtp.getStyledDocument();
StyleContext styleContext = StyleContext.getDefaultStyleContext();
blackAttributeSet = styleContext.addAttribute(styleContext.getEmptySet(), StyleConstants.Foreground, Color.BLACK);
}
@Override
public void insertString(FilterBypass fb, int offset, String text, AttributeSet attributeSet) throws BadLocationException {
super.insertString(fb, offset, text, attributeSet);
styledDocument.setCharacterAttributes(0, jtp.getText().length(), blackAttributeSet, true);
}
@Override
public void remove(FilterBypass fb, int offset, int length) throws BadLocationException {
super.remove(fb, offset, length);
styledDocument.setCharacterAttributes(0, jtp.getText().length(), blackAttributeSet, true);
}
@Override
public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attributeSet) throws BadLocationException {
super.replace(fb, offset, length, text, attributeSet);
styledDocument.setCharacterAttributes(0, jtp.getText().length(), blackAttributeSet, true);
}
}
I've observed that the word wrapping strange behavior does not happen when the custom DocumentFilter is not used. So here are my questions :
- Is there a way to fix this issue ?
- If not, is it possible (and easy ?) to perform syntax highlighting without document filter ?
P.S. I've read about problem with word wrapping and long words in java>6 but this problem is different.