samcp20 samcp20 - 4 months ago 22
Java Question

JTextPane Line Count Including Icons and Components

I've recently been experimenting with the user of JTextPanes for an upcoming project I'll be working on, there have been various posts online detailing how to go about counting the number of lines within the text pane however the solutions I found all seem to fail when inserting Icons or Components into the text pane's document.

The solution I found that worked for plain text was this one (with the solution implemented of course): BadLocationException when using Utilities.getRowStart On hit of Enter key

However once I try to insert a Component (JLabel) or a plain Icon for that matter, the getRowStart() method from Utilities throws a null pointer exception. What I find unusual about this is the Java Doc states that "...This is represented in the associated document as an attribute of one character of content. ", so I assumed it would treat it as any other character but it seems this is not the case.

I've included a code example to replicate the problem if anyone would like to try it. I have a feeling that it just simply isn't possible, which would be a shame.

import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Utilities;

public class Test{

private JFrame frame;
private JTextPane textPane;

private Image img;
private URL imgURL;

public Test(){
frame = new JFrame();
frame.setSize(new Dimension(500,300));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

textPane = new JTextPane();

try {
imgURL = new URL("http://www.freeiconspng.com/uploads/floppy-save-icon--23.png");
img = ImageIO.read(imgURL);
JLabel label = new JLabel(new ImageIcon(img.getScaledInstance(10, 10, Image.SCALE_SMOOTH)));

textPane.insertComponent(label);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

frame.getContentPane().add(textPane);
frame.setVisible(true);

}
public JTextPane getTextPane(){
return this.textPane;
}
public int getLineCount(){
int totalCharacters = textPane.getDocument().getLength();
int lineCount = (totalCharacters == 0) ? 1 : 0;

try {
int offset = totalCharacters;
while (offset > 0) {
offset = Utilities.getRowStart(textPane, offset) - 1;
lineCount++;
}
} catch (BadLocationException e) {
e.printStackTrace();
}

return lineCount;
}
public static void main(String[] args){
Test t = new Test();
t.getLineCount();
}
}

Answer

The problem was solved after the following comment:

It doesn't throw any exception for me once I wrap the content inside your main method inside a EventQueue.invokeLater() call. I.e.:

EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
        Test t = new Test();
        t.getLineCount();
    }
});