I have a dialog where each entry in a JTree has its corresponding options in a different panel, which is updated when the selection changes. If options for one of the entries is set to an invalid state, when the user attempts to change to a different entry in the tree, I want there to be an error dialog and have the selection not change.
I tried doing this with a valueChangeListener on the JTree, but currently then have to have the valueChanged method call "setSelectionRow" to the old selection if there is an error. So that I don't get a StackOverflow, I set a boolean "isError" to true before I do this so that I can ignore the new valueChanged event. Somehow I have the gut feeling this is not the best solution. ;-)
How would I go about it instead? Is there a good design pattern for situations like this?
Set a TreeSelectionModel which implements the appropriate semantics.
To prevent selection I just subclassed DefaultTreeSelectionModel and overrode all the methods to check for objects that I didn't want to be selected (instances of "DisplayRepoOwner" in my example below). If the object was OK to be selected, I called the super method; otherwise I didn't. I set my JTree's selection model to an instance of that subclass.
}
Here is my solution.
In a JTree subclass:
Then in the tree using class:
The mechanism starts at the earliest possible place. Mouse action intercepted, the path to-be-selected is advertised to VetoableChangeListeners. In the concrete VCL the changing property is examined and if it is the lead selection, veto logic is checked. If vetoing is needed, the VCL throws PropertyVetoException, otherwise mouse event handling goes as usual and selection happens. In short, this makes lead selection property become a constrained property.
Stumbled across this thread while investigating a solution for the same problem. First, let me tell you things that didn't work. I attempted to register MouseListeners and all of that with the tree. The problem was that the TreeUI's mouse listeners were getting to the process the event before my JTree did, meaning it was too late to set a flag or anything like that. Besides that this solution produced some ugly code and I would generally avoid it.
So now for the actual solution!
After using a few Thread.dumpStack() calls to get a stack dump, I found the method I was looking to override. I extended the BasicTreeUI and overrode the "protected void selectPathForEvent(TreePath path, MouseEvent event)".
This will give you access to the mouse event that caused the selection before the selection actually occurs. You can then use whatever logic you need to either event.consume() and return if you want to stop the selection, do whatever selection you want or pass it up for default processing by calling super.selectPathForEvent(path, event);
Just remember to set the UI you created in JTree. That mistake wasted a few minuets of my life ;-)
I did not find a better way, but this approach works fine for me. I know in Delphi it was a very convenient event: "before changing selection" where you could very easily stop changing selection.
here is my java code with prevention of infinite recursion problem
always remember to remove all listeners you added, to prevent memory leaks.
here is another approach:
Not sure it's best practice, but maybe you could put a FocusListener on the component(s) you want to validate... call your validation when the event is called and then consume then event if you don't want the focus to be moved because the validation fails?
Later Edit:
At least with java 8 (I didn't check earlier versions) this solution won't work, because the FocusEvent appears not to be a low-level event. Hence it cannot be consumed. See Method AWTEvent.consume()