views:

298

answers:

3

I am trying to create a very simple chat window that simply has the ability to display some text, which I add to from time to time. However I get the following run time error when attempting to append text to the window:

java.lang.ClassCastException: javax.swing.JViewport cannot be cast to javax.swing.JTextPane
    at ChatBox.getTextPane(ChatBox.java:41)
    at ChatBox.getDocument(ChatBox.java:45)
    at ChatBox.addMessage(ChatBox.java:50)
    at ImageTest2.main(ImageTest2.java:160)

Here is the class to handle the basic operations:

public class ChatBox extends JScrollPane {

private Style style;

public ChatBox() {

    StyleContext context = new StyleContext();
    StyledDocument document = new DefaultStyledDocument(context);

    style = context.getStyle(StyleContext.DEFAULT_STYLE);
    StyleConstants.setAlignment(style, StyleConstants.ALIGN_LEFT);
    StyleConstants.setFontSize(style, 14);
    StyleConstants.setSpaceAbove(style, 4);
    StyleConstants.setSpaceBelow(style, 4);

    JTextPane textPane = new JTextPane(document);
    textPane.setEditable(false);

    this.add(textPane);
}

public JTextPane getTextPane() {
    return (JTextPane) this.getComponent(0);
}

public StyledDocument getDocument() {
    return (StyledDocument) getTextPane().getStyledDocument();
}

public void addMessage(String speaker, String message) {
    String combinedMessage = speaker + ": " + message;
    StyledDocument document = getDocument();

    try {
        document.insertString(document.getLength(), combinedMessage, style);
    } catch (BadLocationException badLocationException) {
        System.err.println("Oops");
    }
}
}

If there is a simpler way to do this, by all means let me know. I only need the text to be of a single font type, and uneditable by the user. Aside from that, I just need to be able to append text on the fly.

+2  A: 

You have two options:

  1. Store the JTextPane in a member variable and return that inside getTextPane().
  2. Modify getTextPane to return the JViewPort's view, like this

    return (JTextPane) getViewport().getView();
    

See the Swing tutorials for more detail.

Also, as camickr (and the tutorials) pointed out, using add with a JScrollPane is incorrect. You should be either passing the component to the constructor or using setViewportView.

As a side note, I try not to subclass Swing components unless it's absolutely necessary (preferring composition over inheritance). But that's not particularly relevant to the question.

Michael Myers
I think it's a typo, should be `getView()` not `getViewportView()`.
JRL
@JRL: You're right; I just assumed that the Swing tutorials had it right. That kind of hurts my point about reading them.
Michael Myers
Even though the typo is fixed, the problem is that the text pane has not been added to the viewport, so that will not fix the problem.
camickr
+1  A: 
public JTextPane getTextPane() {
    return (JTextPane) this.getComponent(0);
}

this.getComponent(0) is returning the ScrollPane's JViewPort, not your JTextPane. It can't be casted, and so you get your exception.

JRL
This may fix the stated problem, however, the scrollpane won't work since you can't just add the text pane to it. You must add the text pane to the viewport in order for it to work. Then I'm not sure if you are guaranteed that the text pane will be component 0, since you could have scrollbars or row headers added to the scrollpane as well.
camickr
@camickr: I think you're right about the `setViewportView()` part, but note that I'm not proposing a fix, just explaining the exception.
JRL
Yes, I see now.
camickr
+2  A: 

Don't extend a JScrollPane. You are NOT adding any functionality to it.

It looks like the basic problem is that your are trying to add the text pane to the scrollpane. This is not the way it works. You need to add the text pane to the viewport. The easy way to do this is:

JTextPane textPane = new JTextPane();
JScrollPane scrollPane = new JScrollPane( textPane );

or

scrollPane.setViewportView( textPane );
camickr