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.
You have two options:
Store the JTextPane in a member variable and return that inside getTextPane().
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.
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 );
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.
Related
I have a class "MainFrame1" that extends a JFrame and also another class that is a file chooser. Whenever I press one of the JMenuItems in MainFrame1 class, I want the file chooser to open up and load up the text of the chosen file on a JTextArea that was created in MainFrame1 class. This works perfectly fine as I created a separate class implementing an ActionListener. Now my problem is that when I press another JMenuItem I want to do something else to the text in the JTextArea. I have implemented another ActionListener for that in a different class but the problem is that the JTextArea seems to be empty when I do that although I can see the text in there. Thanks in advance.
This is how I have created the JTextArea in the MainFrame1:
showAction = new JTextArea(10,10);
showAction.setEditable(false);
showAction.setFont(new Font("Arial", Font.BOLD, 12));
add(showAction, BorderLayout.NORTH);
And this is my second ActionListener class (also, whenever the text of a file is printed in the JTextArea, the text "loaded up." will also be printed) and I always get the else branch.
public class TransformController implements ActionListener{
MainFrame1 mf;
public TransformController(MainFrame1 mf) {
this.mf = mf;
}
#Override
public void actionPerformed(ActionEvent e) {
String text = mf.showAction.getDocument().toString();
if(text.contains("loaded up.")) {
char[] charText = text.toCharArray();
Parser parser1 = new Parser(charText);
parser1.packageVisitor();
}
else {
System.out.println("Load up a Java file first!");
}
}
}
This seems to be mostly a debugging question: First, find out what's in showAction.getDocument() to see if your menu item just isn't loading it right. Then check (with an IDE or via toString()) that mf.showAction really is the same object in the two cases.
Structurally, there's nothing in Java that prevents you from having a reference to the same JTextArea in two parts of the code, and reading the text out of it for different purposes.
New to the forum and to Java. I am trying to have my JList respond when double-clicked, which I have accomplished. The JList is being populated by a SQL query which is ran when a button in the GUI is pressed. Based on the SQL query, the JList is populated, this is also working.
The issue comes about if I try to update the JList by clicking the button to query SQL again. When I click that, the change initially shows up in the JList, however when I click on that option in the JList it immediately switches back to what it was initially. When I double-click on what appears to be the incorrect name, the value that I have printing in the console reports correctly. So it has the value correct in the console but the rendering in the JList is not correct.
I appreciate any responses, I have combed the forums without any luck. I am new to Java so I'm sure there is quite a bit that isn't perfect with my code. Code is below please let me know if you need more. Thank you.
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
showOption = new JList(options);
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
textPanel.add(showOption);
showOption.revalidate();
showOption.repaint();
MouseListener mouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//JList showOption = (JList) mouseEvent.getSource();
if (e.getClickCount() == 2) {
int index = showOption.locationToIndex(e.getPoint());
Object o = showOption.getModel().getElementAt(index);
System.out.println("Double-clicked on: " + o.toString());
}
}
};
showOption.addMouseListener(mouseListener);
return totalGUI;
}
public static void main ( String args[] )
{
//JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("[=] JTextField of Dreams [=]");
GUI_TextField demo = new GUI_TextField();
frame.setContentPane(demo.createContentPane());
//frame.setContentPane(demo.results(message));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(510, 400);
frame.setVisible(true);
}
Three things jump out at me immediately.
You're creating a new JList each time
You're manually setting the size and position of the JList
You're not removing the previous JList
For example...
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
// Create new JList
showOption = new JList(options);
// This is ill advised
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
// What about the last JList?
textPanel.add(showOption);
This raises a number of possibilities, the likely one is that you are covering over the previous list, which is being brought to the front when textPanel is validated and painted.
Swing follows (loosly) the MVC paradigm (and for more details)
So instead of re-creating the view each time, you should simply re-create the model, for example...
public JPanel results(StringBuilder message)
{
DefaultListModel model = new DefaultListModel();
model.addElement(message);
showOption.setModel(model);
If showOption isn't created initially before this method is called, you should consider putting in a if statement to detect when showOption is null and initialise it appropriately.
You should also avoid using setLocation and setSize. Swing has being designed to operate with the use of layout managers, these make it possible to define workflow and general layout that can be used across multiple platforms.
Take a look at How to use lists and Laying Out Components Within a Container
I am rendering some images that are layered in a JEditorPane. I've read that JEditorPane is pretty rocky at best, however I am hoping that this is an issue with either my HTML code or something else. Here's how my content looks in the browser:
And how it looks in a JScrollBar(JEditorPane):
The HTML code: http://pastebin.com/EixG3WLH
The Java code:
File f = new File("index.html");
JEditorPane jep = new JEditorPane(f.toURI().toURL());
JScrollPane sp = new JScrollPane(jep);
JFrame frame = new JFrame();
frame.add(sp);
jep.setEditable(false);
frame.setVisible(true);
frame.setSize(500, 500);
frame.setTitle(wpj.getParse().getTitle());
I'd really rather not use FlyingSaucer if this issue can be resolved in a JEditorPane!
You can do it... but it's not really simple... because JEditorPane doesn't have CSS absolute positioning... so, you must first at all, recognize if some element had the position:absolute or position:fixed attribute extending the ViewFactory, something like:
public class ExtendedHTMLEditorKit extends HTMLEditorKit{
//.... other code here
public class MyHTMLFactory extends HTMLFactory{
//other code here
#Override
public View create(Element elem) {
if (isLayered(elem)){ //it means, it has position attribute
return new PositionedView(elem);
}
else
return super.create(elem);
}
boolean isLayered(Element elem){
SimpleAttributeSet sas = new SimpleAttributeSet(elem);
StyleSheet styles = (HTMLDocument elem.getDocument).getStyleSheet();
Tag tag = element.getAttributes().getAttribute(AttributeSet.NameAttribute);
sas.addAttributes(styleSheet.getRule(tag, element));
return sas.isDefined("position")
&& !sas.getAttribute("position").toString().equalsIgnorecase("static");
}
}
}
In this case, we need then to build a correct view for your element... I don't know if you're only positioning images (in this case, it could be simple) or a lot of things... I can see on your code, you're using divs...
Let me explain more or less what I do: I've created a ComponentView, and returning as a component a new JEditorPane, where I put the innerCode of the original element... and after that, move it on correct position of parent editor...
To synchronize this is really dificult to allow edit, but if you only whant to use them to display, it must be more simple...
ok.. the view must be like:
public class PositionedView extends ComponentView{
private JEditorPane view;
private JEditorPane parent;
#Override
public Component createComponent(){
if (view == null){
view = new JEditorPane();
}
String innerText = dumpInnerText(getElement());
view.setText(innerText);
view.setLocation(getAbsoluteX(), getAbsoluteY());
parent.add(view);
}
#Override
public void setParent(View parent) {
if (parent != null) {
java.awt.Container host = parent.getContainer();
if (host != null && host instanceof JEditorPane) {
parent = (JEditorPane) host;
}
}
super.setParent(parent);
}
protected int getAbsoluteX() {
//search for the attribute left or right and calculate the position over parent
}
protected int getAbsoluteY(){
//search for the attribute top or bottom and calculate the position over parent
}
protected String dumpInnerText(Element element){
//there are several ways to do it, I used my own reader/writer,
//because I've need add special tags support...
}
}
I hope this helps you... Ah! there are another thing: if you do this, you must secure your view is not opaque, and it means, all the view elements, on the other case, you will have a blank rect for your elements.
Another thing, you maybe need to check for the correct dimension of the view... do as getAbsoluteX / getAbsoluteY to obtain width / height attributes.
JEditorPane is not so good with CSS absolute positioning. I think you are trying to achieve more with JEditorPane than it is capable of delivering.
I don't think this is a new issue. But it seems that there is an error that comes up whenever an ordered/unordered list in JTextPane (EditorKit -> HTMLEditorKit, Document -> HTMLDocument) is deleted from all the way down to the top using the backspace key. The following is the exception thrown from the getText () method of GlyphView.
Exception in thread "AWT-EventQueue-0" javax.swing.text.StateInvariantError: GlyphView: Stale view: javax.swing.text.BadLocationException: Invalid location
I can provide a SSCCE for this. But it is not very difficult to simulate. Just use a JTextPane with HTMLEditorKit and HTMLDOcument model set inside it. Either use the custom "InsertOrderedList" action or have some way to insert the string
<HTML><HEAD></HEAD><BODY><UL><LI></LI></UL></BODY></HTML>
which will result in the insertion of an ordered/unordered list inside the text pane.
The weird parts of this bug are as follows:
Once you start deleting the characters (and if you happen to have lines below the bulleted list), the characters will get deleted until you hit the last character of the last bullet item. Once you reach this, the caret just refuses to move up and the error from GlyphView gets thrown.
Sometimes what happens is that after you have deleted most of the characters - you still won't be able to delete the first bullet of the list. It just hangs on until you do a ctrl+a and then backspace.
I have seen these bugs in almost all Java based HTML editors available online except for JWebEngine, where this behavior is not present. Unfortunately JWebEngine is not open sourced and hence I can't take a peek inside their code to see as to how they have solved this problem.
My guess is that the notification that is coming from the HTML document model has some problem due to which the cursor positioning code is not working correctly. I have also searched the Sun bugs database to check if this particular issue has been raised but could not find any (though I have seen quite a few bugs which are very similar to this). Also I am very sure that someone must have noticed this before and must have brought it to the Swing team's attention.
Does anyone working with Swing (particularly text) part know as to if this issue has been raised with Sun or if there is any known workaround that has been found to mitigate the problem?
Though it is possible that the user is still able to delete the lists from the pane using the mouse selection, not having the option to do the same using the backspace key just seems very weird.
SSCCE is now attached. To repro the bug pls. follow the steps as shown in the attached fig.
Add a line of text. Then add 2/3 bullet items by clicking on the button above the text pane. Now place the caret at the end of the last char of the last bullet and keep on pressing the entire backspace key until all the chars get deleted.
Observed behavior: the last bullet will hang on (won't get deleted) and the exception will be thrown (as mentioned above)
Expected: No exception and the contents of the text pane should get cleared.
public class Test {
static final JFrame frame = new JFrame ();
static final JTextPane textPane = new JTextPane ();
static EditorKit kit;
static JButton listButton;
public static void createAndShowGUI () {
//Create frame
frame.setSize(400, 600);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
//Customize text pane visual properties
textPane.setSize(300, 500);
textPane.setLocation(50, 50);
//customize text pane non visual properties
kit = new CustomEditorKit ();
textPane.setEditorKitForContentType("text/html", kit);
textPane.setContentType("text/html");
Action[] actions = ((HTMLEditorKit) kit).getActions();
Action action = null;
for (int i = 0; i < actions.length; i++) {
action = actions [i];
if (action.getValue(Action.NAME).equals("InsertUnorderedList")) {
break;
}
}
listButton = new JButton (action);
listButton.setText("List");
listButton.setSize(100, 20);
listButton.setLocation(100, 10);
listButton.setVisible(true);
/* Add button and text pane to frame */
frame.add(listButton);
frame.add(textPane);
}
public static void main(String[] args) {
try {
EventQueue.invokeAndWait(new Runnable () {
#Override
public void run() {
createAndShowGUI ();
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
static class CustomEditorKit extends HTMLEditorKit {
#Override
public Document createDefaultDocument () {
return new HTMLDocument (this.getStyleSheet());
}
}
}
I used this
action=new HTMLEditorKit.InsertHTMLTextAction("test", "<UL><LI><P>\n</P></LI></UL>", HTML.Tag.BODY, HTML.Tag.UL);
instead of default action in your example to provide correct structure.
Works fine for me. (Win 7, Java 1.6)
For some reason my HTML page is not appearing 100% on screen when it should, it looks like a timing issue to me. If I remove scrollpane and use just EditorPane it works ok.
What kind of code should I add below to force java applet screen to redraw/refresh and can I somehow wait until all images were really loaded ok? Currently images are drawn a bit after text is visible on GUI.
(the gray goes away and missing text appears when I minimize+maximize window.)
I use SynchronousHTMLEditorKit as m_editorPane.setEditorKitForContentType
private JEditorPane m_editorPane = new JTextPane();
private JScrollPane m_scrollPane = new JScrollPane();
....
JEditorPane.registerEditorKitForContentType( "text/html", "SynchronousHTMLEditorKit" );
m_editorPane.setEditorKitForContentType( "text/html", new SynchronousHTMLEditorKit() );
m_editorPane.setPage(ResourceLoader.getURLforDataFile(file));
m_scrollPane.getViewport().add(m_editorPane);
m_scrollPane.validate();
m_scrollPane.repaint(); <-- does not seem to solve this
add(m_scrollPane);
/// add( m_editorPane) <-- this WORKS !!
SynchronousHTMLEditorKit is defined as:
public class SynchronousHTMLEditorKit extends HTMLEditorKit {
public Document createDefaultDocument(){
HTMLDocument doc = (HTMLDocument)(super.createDefaultDocument());
doc.setAsynchronousLoadPriority(-1); //do synchronous load
return doc;
}
Try moving the validate and repaint calls to the bottom, after the add, and call them on the container, not the scrollpane
add(m_scrollPane);
validate();
repaint();
What happens if you don't use a SynchronousHTMLEditorKit? Your code works perfectly for me without it.