in my swing application I have a combo box with an ItemListener that does X if the user changes the value (via itemStateChanged()).
However I also have a different function that changes the value of that combo box. In this case I do not want X to be done.
Is there a way to find out if the state change was caused by user interaction or from a function?
Thank you!
Edit: I used the flag method. Thanks for the quick answers. I just want to add, that itemStatechanged is actually called twice, once for deselection and once for selection. This needs to be dealt with otherwise the flag won't have any effect. The problem is discussed here.
There are 2 ways to do the check:
Define a flag isUser. The flag is true by default. Before changing programmatically set it to false and reset after setting the combo-box value. In the listener just check the flag and skip the action if necessary.
Keep a reference to the listener and remove it before setting value, adding after.
From what I understand you could very easily sort out your problem using a flag.
Just make a boolean flag, e.g. isDoneByMethod which will be on entrance to a method set to true and at the end set to false and between these two do the operation on the combobox. Then inside the itemStateChanged() check for the value of the isDoneByMethodflag and act accordingly.
I have the similar kind of situation where I need to differentiate two cases. Both options discussed here (flag and removal/addition of listener) do not work all the time, as the item listener is asynchronously called on EDT thread. However remove and adding the particular item listener is more safe . To further ensure that all the previous actions will be dealt before you add the item listener, I use fireEvents() just before adding the listener again.
Related
I'd like to update GUI elements (JComboBox, JLabel, etc.) from code which shouldn't trigger change event. Is it possible to find out from java.awt.event.ActionEvent or java.awt.event.ItemEvent if the change was caused by an user or by running code like this?
combo.setSelectedItem("my item")
The answer is: no.
But in some cases you can try to analyze the current InputEvent. To get it, use EventQueue.getCurrentEvent(). For example if user has triggered the change on clicking of another component, you can compare the component of the input event and the component of the action event (OK I know: it's unsafe. But in some cases it can help to avoid incrementing of application complexity).
For a button you can get the event modifiers:
int buttonModifiers = evt.getModifiers();
If the button event was generated with a call to doClick() the modifier is 0, otherwise not.
By the way, you can find out such differences relatively easy by logging / printing using evt.toString()
.
I want to programatically set an Eclipse plugin action (button) such as this here:
For example, if the user presses it, I do not want it to toggle off under certain conditions.
This code here creates the action (button):
class MyAction extends Action {
public MyAction() {
super(NAME, IAction.AS_CHECK_BOX);
}
...
Thread.sleep(100); wait a little bit incase there is a thread update issue
if (condition)
setChecked(true); // does not work, it does not force the button to appear as depressed. It just keeps toggling.
...
}
For some reason setChecked(true) does not work.
The problem here I believe is the fact you're calling setChecked inside of Action.run(). Since one of the effects of clicking a checkbox is to check it, you're sneakily trying to cancel the action while it's going on. In fact, I bet the framework code sets checked to true after Action.run() returns, so it's stomping on your change.
Action has a way of letting you control this in a more defined way. Rather than implementing .run(), implement .runWithEvent(Event). This function passes in an Event object that you can use to have finer grained control.
In this case, I think you want to set Event.doit to false. From the docs:
Setting this field to false will cancel the operation.
Another option
Depending on how your condition is calculated, you may want to preemptively enable/disable your checkbox when it changes. This way you can also prevent a tooltip or similar to explain why it's disabled.
I have a general question regarding listeners.
Lets say I have two JTabbedPanes and both have a ChangeListener. They are both displayed and I want them both to show the same pane (index) so when a user changes the selected pane in one the other changes too.
In brief, one JTabbedPane listener changes the other JTabbedPane using setSelectedTab().
Obviously, the first listener will activate the second listener and the second will reactivate the first in an endless operation.
This will be solved with booleans.
Is there a smarter way to do it?
Is there a way to change a tab without triggering the Listener?
Is there a way to activate the listener only when a user changes it and not the code?
Thank you.
BTW: I always have the same questions with buttons. But with buttons I take the code from the listener and put it in a method. when One button needs to activate a button it calls its code. But in JTabbedPane it is different.
The simple solution is to act only when necessary. For example:
if(currentTab != desiredTab) {
// change tab
}
That will prevent an infinite loop.
If you need to be able to switch the behavior on and off, then using a boolean flag isn't a bad way to go about it. The alternative is the remove the listener, using removeChangeListener. The flag may be more performant as it may avoid memory allocation and deallocation, but a lot depends on the other details of your situation.
share the selectionModel, like
secondTabbedPane.setModel(otherTabbedPane.getModel());
I have a JTree, a JTable and a JList which displays the same set of objects, but in different order and with different information. If an item is selected from one of the Component, I want to select the same object on the other two Components (meaning they should be highlighted). Naturally I monitor the selection events with a Listener. Here is the problem, when a Component retrieves the selected object, I'll have to make sure the object is selected on the other Components by calling selection methods on them. This, will then notify the selection listeners on the other two components. But each of those events will in turn call selection events on components other than itself, causing an infinite loop going among the three Components.
I see one solution is to use a boolean flag, and make the listeners not propagate the selection if the flag is set. However, this seems cumbersome and not elegant. Is there a way to simply tell JTree, JTable and JList to make the selection but not fire any events (as oppose to fire an event and then catching and stopping it with a boolean flag)?
Take a look at SharedModelDemo. I think it does what you're looking for.
I would use a flag indicating whether it's user changes or internal changes but yu can also remove listeners before selection call and add them after to prevent events firing.
I'm looking for a listener that fires ONLY when the user, the one who's using the program, selects an item in the JComboBox. I don't want to use ActionListener or ItemListener because those also fire when I select an item through the program. And I can't use MouseListener either because it only fires when I click the JComboBox, not when I select an item.
I was wondering what the easiest way to do this is? Currently, my solution is messy. When I change the selected item of the jcombobox through code, I set a flag to true. And in my action listener, it only executes if the flag is false.
A) I would recommend you to temporarily remove the listener when you perform the selection programatically.
B) If your programatic change is not an effect of another GUI event you could solve it the following ugly/non-robust/error-prone/"hacky" way: Check EventQueue.isEventDispatchThread() to find out if the click was triggered by the GUI thread (the user).
C) (Oops I just reread your question and saw that you've already discovered the method described below. Basically I would say that this (or the the method described above) is your best alternative.)
Another option is to have a boolean flag called something like nonUserSelection which you set to true before you select a value programatically and reset to false afterwards. In the action listener you simply add an
if (nonUserSelection)
return;