• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

General JTextPane help

 
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I am working on a program that makes use of a JTextPane inside a JScrollPane, and have hit some snags along the way (As seems to often be the case with learning any aspect of javax.swing for the first time).

1. Perhaps I'm just not looking carefully enough, but I could not find any convenience methods in JTextPane, or the classes it contained, to simply append content. There is only "setText" and "insertText". Am I missing something, or is that that? I've actually been using the setText method so far since I didn't notice the insertText method until recently, but I'm assuming the latter would be more efficient in cases where I am just constantly appending more text to the original?

2. Since this text pane is the viewport for a chat program, I am trying to achieve standard scrolling behavior where the scroll bar moves down to show new messages if and only if the scroll bar is already at the lowest point. I've done a lot of searching on how to achieve this, and some of the results even linked here, but the best solution I've found so far only half works.

The code:

chatView is a JScrollPane. The problem with this method is that even though the caret is set to the maximum value, the scroll bar will actually just be ever-so-slightly away from all the way down. Then, when another message comes in, it doesn't think it should continue to scroll down since it's only close to bottom, but not actually.

What it looks like after the caret reset:



What it should look like:


3. My final issue isn't so much an issue with coding its self, as much as I just need some guidance. I'm trying to figure out how to go about dynamically adding styled content to the JTextPane, and while Java/Oracle tutorials are usually very well written, it seems like even the devs aren't entirely sure how to describe some javax.swing aspects thoroughly... at any rate, if someone has some working examples, or knows of a well written tutorial, I would appreciate it. I'm not looking forward to hours of trial and error just trying to figure out JTextPanes.

Thanks in advance for any help!
 
Bartender
Posts: 825
5
Python Ruby Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
1)
You can use underlying Document of your component, that contains insertString method.


2)


3)
Well, besides Tutorial (and perhaps recommendation of books I used) I can't help you with this one.
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kemal Sokolovic wrote:1)
You can use underlying Document of your component, that contains insertString method.


2)



I'm aware of the ScrollBar's getValue/Max/Min methods (you can see that I use it in my code) but it doesn't appear to work at all how one would expect when setting the value. Or maybe I was just way too tired the last time I tried. Either way, working example code would be appreciated if setValue is the proper method to be using for this after all. I can definitely say that I tried the obvious setValue(verticalScrollBar.getMaximum()), and as far as I can tell, it's not so simple.

If you know of any online tutorials (besides the official Javadocs and Oracle articles, which I've already looked through) then I would definitely appreciate it.
 
Kemal Sokolovic
Bartender
Posts: 825
5
Python Ruby Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well, I guess I didn't test it well. I put a JScrollPane with text to append, and a button at the bottom of frame that scrolls down the pane with code that I showed you in the previous post. And it worked! On the other hand, when I put the same code after the one that appends the text, it doesn't work.
So I tried to tweak it out a little, until we wait for someone else to tell us about that strange behavior, I'm intrigued now.

Perhaps not the best solution, but it works.
 
Ranch Hand
Posts: 4632
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
after the code to append new text, add this line

textPane.setCaretPosition(textPane.getDocument().getLength());

not tried it on a textPane, but that works fine for other text components
 
Kemal Sokolovic
Bartender
Posts: 825
5
Python Ruby Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Michael Dunn wrote:after the code to append new text, add this line

textPane.setCaretPosition(textPane.getDocument().getLength());

not tried it on a textPane, but that works fine for other text components


Tried that one, not working. There is still that small offset at the bottom.
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kemal Sokolovic wrote:Well, I guess I didn't test it well. I put a JScrollPane with text to append, and a button at the bottom of frame that scrolls down the pane with code that I showed you in the previous post. And it worked! On the other hand, when I put the same code after the one that appends the text, it doesn't work.
So I tried to tweak it out a little, until we wait for someone else to tell us about that strange behavior, I'm intrigued now.

Perhaps not the best solution, but it works.


That would explain why I automatically assumed your solution was broken. In my program, the scroll code is ONLY called immediately after changing the content of the JTextPane. You're saying the code works just fine if it's run alone? That IS weird! Not that I'm at all surprised. Yay Java Swing! Thanks for your help so far.
 
Kemal Sokolovic
Bartender
Posts: 825
5
Python Ruby Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The last code I gave you works well for your purpose, just invoke scrollToBottom() after you append text at the end of the pane.
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kemal Sokolovic wrote:The last code I gave you works well for your purpose, just invoke scrollToBottom() after you append text at the end of the pane.


I'm sorry, but it seems like there are still some issues... Again, here is my code:



This solution also seems to "half work" but in a very different way. On some updates, it basically functions although it leaves a gap like the previous solution, other times it works just fine (sometimes even when there is a gap, har), and other times it won't work at all...

Update: I went and converted my code to use the insertText method instead of setText. That created a better situation, but still not perfect. Now, it works at first, but as more updates come and the method is called again repeatedly, the scroll bar starts to "slide up" a bit more each time. I can slide it down manually, and that will reset it, allowing it to work for a few times before sliding up again.

The modified code:
 
Kemal Sokolovic
Bartender
Posts: 825
5
Python Ruby Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Since it works well at me, I would suggest you debug and check if that if evaluates to true (the one in which you invoke scrollToBottom()). You maybe don't even need to perform all those checks, because given method will work properly in any case.
 
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

In my program, the scroll code is ONLY called immediately after changing the content of the JTextPane. You're saying the code works just fine if it's run alone? That IS weird! Not that I'm at all surprised. Yay Java Swing!


The problem is once you add something to the text pane the size of the panel changes and hence the size of the scrollbar. You need to check if the scrollbar is at the bottom before you add anything else to the pane, then add the text and then move the scroll bar if required.

This is not correct because the value to set to put the slider button is at the bottom of the scrollbar is the scrollbar's maximum value less it's extent (the size of the slider button).

Does this do what you want?
Note I needed to add the scroll bar slider button repositioning code to the event queue so it happens at a future time or the repositioning is overwritten by the default action of adding text to the text pane.

 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Kemal Sokolovic wrote:Since it works well at me, I would suggest you debug and check if that if evaluates to true (the one in which you invoke scrollToBottom()). You maybe don't even need to perform all those checks, because given method will work properly in any case.


I'm pretty sure it evaluates to true, because it worked when I was using the caret reset method, and even now I can see it's doing something - just not the right thing. Anyways, I do need all those checks because I do NOT want to reset the scroll bar if the user isn't ALREADY at bottom.

Tony Docherty wrote:The problem is once you add something to the text pane the size of the panel changes and hence the size of the scrollbar. You need to check if the scrollbar is at the bottom before you add anything else to the pane, then add the text and then move the scroll bar if required.

Isn't that the order I'm currently doing it in?

Tony Docherty wrote:
This is not correct because the value to set to put the slider button is at the bottom of the scrollbar is the scrollbar's maximum value less it's extent (the size of the slider button).

Does this do what you want?
Note I needed to add the scroll bar slider button repositioning code to the event queue so it happens at a future time or the repositioning is overwritten by the default action of adding text to the text pane.


I have seen that eventQueue code in some other places, but it has always seemed iffy to me.... especially since as far as I can tell in my code, I DO set the bar only after text has been appended. Sorry about this misunderstanding as to what the issue is. not to mention, the "run later" method seems sort of hack-ish. There isn't a direct way to just push the bar to the bottom, regardless of whatever that value may be? That seems like horrible planning on the part of Java's developers.
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I have seen that eventQueue code in some other places, but it has always seemed iffy to me


If that seems iffy you need to read a tutorial on how Swing works.

not to mention, the "run later" method seems sort of hack-ish


It's a good job I have a sense of humour, some people would get offended by having their attempt to help described as "iffy" and "hack-ish".
To be honest I'm not 100% sure why this works the way it does - I would need to look through the Swing classes to see how they all work but I suspect it works as follows. When you add text to the TextPane document an event is fired to say the document has changed. The text pane updates it's display and fires an event to say it's size has changed. The JScrollPane listens for size change events from it's view and updates it's view and scroll bars. Now if any of these actions call any non-thread-safe methods then the action is enqueued to the EventQueue to be run on the EDT (as all Swing methods must be) when it is next free. As our code is being run on the EDT the current action must complete before the pending update can be run. Hence, unless we enqueue our updates of the scrollbar position the position we set will be overwritten when our method completes and the EDT runs the next event which happens to be part of the chain of events described above. However, by enqueueing our scrollbar repositioning code the default scroll bar repositioning code is run and then our repositioning code is run afterwards.

It appears the default action if you don't attempt any repositioning of the scrollbar is move it to a little short of maximum after every append.

BTW Did you run the code I supplied and does it work the way you want?
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:

I have seen that eventQueue code in some other places, but it has always seemed iffy to me


If that seems iffy you need to read a tutorial on how Swing works.


It's quite likely, I've never liked Javax.Swing much and I try my best to understand just enough to use it. :P My main worry is that since this is based on an event queue with an arbitrary number of events ahead of it, what happens if this code needs to change for a future Java update? As an example, in one of the solutions I saw online, the code had TWO "run later" sequences, one nested in the other, with some explanation along the lines of how you needed to run it later twice to get around some previous steps. Now your code says I only need it once. Perhaps in some cases I might need it three times? Maybe the other guys are just idiots and you really only need it to run once ever regardless of the situation, but I would definitely like some clarification on this...

Tony Docherty wrote:

not to mention, the "run later" method seems sort of hack-ish


It's a good job I have a sense of humour, some people would get offended by having their attempt to help described as "iffy" and "hack-ish".
BTW Did you run the code I supplied and does it work the way you want?


Well, I'm very glad you weren't offended, that wasn't at all my intention. I was just trying to point out Swing's issues, not yours... Unless you personally helped write the javax.swing library yourself, in which case, yea, you should be ashamed of yourself! Hahah! But seriously... uh, nope, I regret to inform you this solution has the same issue as the previous one - It appears to work for a few times at first (and you might even have to "kick it into place" at first when the scroll bar first appears) but after some time, a gap appears between the scroll bar and the bottom, and that gap widens on each successive updateChat() operation without any intervention from the user's mouse.

Thanks again to everyone's help though.
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

But seriously... uh, nope, I regret to inform you this solution has the same issue as the previous one - It appears to work for a few times at first (and you might even have to "kick it into place" at first when the scroll bar first appears) but after some time, a gap appears between the scroll bar and the bottom,

Maybe I don't fully understand what your problem is then because once the scroll bar slider is moved to the bottom it stays there, at least on my system it does. When you say after some time it fails at approximately what line number does this tends to happen as I've run it multiple times adding thousands of lines each time and not seen any issues.

BTW Yes you will have to move the slider to the bottom when the scrollbar first appears as it doesn't start at the maximum position although you could add a listener to handle this situation.
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:

But seriously... uh, nope, I regret to inform you this solution has the same issue as the previous one - It appears to work for a few times at first (and you might even have to "kick it into place" at first when the scroll bar first appears) but after some time, a gap appears between the scroll bar and the bottom,

Maybe I don't fully understand what your problem is then because once the scroll bar slider is moved to the bottom it stays there, at least on my system it does. When you say after some time it fails at approximately what line number does this tends to happen as I've run it multiple times adding thousands of lines each time and not seen any issues.

BTW Yes you will have to move the slider to the bottom when the scrollbar first appears as it doesn't start at the maximum position although you could add a listener to handle this situation.


I'm sorry, I guess I'm not explaining it well. It isn't literally a time-based thing. But after adding content to the pane a certain number of times, a gap that gradually widens appears. I can go and record it if you really want but I promise this is happening.

Here is the FULL GUI class code, linked to a Pastebin so that this isn't a massive post: http://pastebin.com/3tSvEJQx
This is the only other class besides the GUI its self which calls the updateChat method: http://pastebin.com/x33qvxqx
And just for good measure though I doubt it's necessary, the network class: http://pastebin.com/1HyL0xeQ

The only thing of note which might be different from your case example is that since I'm sending and receiving messages over localhost, updateChat() is being called very quickly twice in a row. However, since I synchronized that statement, I'm assuming that shouldn't matter?
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I don't disbelieve you, I'm just trying to understand the difference between what you are doing and what I was doing.
I'm short of time now but will look at the code later and see if I can spot any issues.
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
All calls to Swing methods must be made on the Event Dispatch Thread (EDT) unless the method is marked as being thread safe. Inserting text into a Document object is not thread safe and therefore must be called on the EDT which you are not doing.
The simplest solution to test if this works for your code is to wrap the call in ChatManager to gui.updateChat() in a Runnable object and pass that to the EventQueue. ie:

Yes I'm afraid it's another one of those
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:All calls to Swing methods must be made on the Event Dispatch Thread (EDT) unless the method is marked as being thread safe. Inserting text into a Document object is not thread safe and therefore must be called on the EDT which you are not doing.
The simplest solution to test if this works for your code is to wrap the call in ChatManager to gui.updateChat() in a Runnable object and pass that to the EventQueue. ie:

Yes I'm afraid it's another one of those


Ahh... well I think I better understand the purpose of using the EventQueue now. To clarify, the methods that Swing uses INHERENTLY go against proper thread management (I'm guessing Swing classes use their own threads, throwing any attempt to sync them out the window?) so we need force all Swing methods into a package that will ensure the entire procedure is run before any code which syntactically follows, correct? Or am I getting this wrong?
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

To clarify, the methods that Swing uses INHERENTLY go against proper thread management (I'm guessing Swing classes use their own threads, throwing any attempt to sync them out the window?)


Not quite. Swing is single threaded and that thread is the EDT. For example when you click a button the event is dispatched on the EDT. Therefore, to ensure calls to Swing methods are only ever run on a single thread you enqueue tasks to the EventQueue and let the EDT do the work when it is next free.

Some methods are marked as thread safe and in these cases if the calling thread is the EDT it just does the work otherwise the call is enqueued to the EventQueue.

Note: there are 2 ways of enqueueing tasks to the EventQueue: invokeLater() and invokeAndWait().
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:

To clarify, the methods that Swing uses INHERENTLY go against proper thread management (I'm guessing Swing classes use their own threads, throwing any attempt to sync them out the window?)


Not quite. Swing is single threaded and that thread is the EDT. For example when you click a button the event is dispatched on the EDT. Therefore, to ensure calls to Swing methods are only ever run on a single thread you enqueue tasks to the EventQueue and let the EDT do the work when it is next free.

Some methods are marked as thread safe and in these cases if the calling thread is the EDT it just does the work otherwise the call is enqueued to the EventQueue.

Note: there are 2 ways of enqueueing tasks to the EventQueue: invokeLater() and invokeAndWait().


Ok, I understand now. I must admit, I completely misunderstood the purpose of the EventQueue before, but I think it makes sense now. Later today I'll hopefully have time to play with my code. Regardless of when, I'll be sure to report on the results.
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I must admit, I completely misunderstood the purpose of the EventQueue before, but I think it makes sense now


So are you saying you no longer think my code is quite so iffy and hackish

Regardless of when, I'll be sure to report on the results.


I look forward to it.
 
Jesse Weiman
Greenhorn
Posts: 12
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tony Docherty wrote:

I must admit, I completely misunderstood the purpose of the EventQueue before, but I think it makes sense now


So are you saying you no longer think my code is quite so iffy and hackish

Regardless of when, I'll be sure to report on the results.


I look forward to it.


I never thought your code is hackish, I think Java's implementation of this system, and how it forces you to use to EventQueue for certain things, is hackish. ;P

Anyways, sorry for the wait. Combination of life in general, and the fact that while this thread was going, I had apparently made a lot more code-breaking changes in my program then I thought. I'm trying to finish up the changes/additions I've made so that I can test out the scroll bar functionality, and get back to you guys!
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I had apparently made a lot more code-breaking changes in my program then I thought.


We've all been there.
After doing this sort of thing a few times you start taking snapshots of your code before making changes or better still use a revision control system.
 
I am going down to the lab. Do NOT let anyone in. Not even this tiny ad:
a bit of art, as a gift, that will fit in a stocking
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic