Is it possible that, for example, a JButton calls actionPerformed() of an ActionListener attached to it while the button itself is hidden or has been disabled?
Like, the user clicks the button, an event gets added to the Event Queue, and in a previous event the button gets disabled.
I'd think that this would not lead to an actionPerformed(), because the user merely submitted a click- or press-event that checks all this stuff in the current JFrame.
But does anybody know if there is any case where such an unwanted situation happens? Of course, always provided that you don't do anything with Swing objects outside of the EDT.
Edit for anyone looking at this: Yes, it can indeed happen under certain circumstances. I should post an example as an answer at some point.
From the JavaDocs
public void setEnabled(boolean enabled)
Sets whether or not this component is enabled. A component that is
enabled may respond to user input, while a component that is not
enabled cannot respond to user input. Some components may alter their
visual representation when they are disabled in order to provide
feedback to the user that they cannot take input.
For more info
http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#setEnabled%28boolean%29
INFO : JButton extends AbstractButton which extends JComponent
Just use a conditional statement and tell the app to enable jButton when the conditions are met.
For example:
private buttonNameActionPerformed(java.awt.evt evt){
if(condition.equals(something)){
jButton.setEnabled(true);
}
else{
jButton.setEnabled(false);
}
}
Related
How can I check if currently any mouse button is pressed and if so, which one it is?
The thing is that I need to use this kind of information in MouseListener.mouseEntered(). I checked MouseEvent but I could not find the method which would help me.
The getButton() method seems to only return value if there has been change in buttons' state.
Is there a way to find this out without manually keeping track of this somehow vie MouseListener.mousePressed()/mouseReleased() methods.
How can I check if currently any mouse button is pressed and if so, which one it is?
Presumably you want to invoke specific code depending on the button pressed so you can do something like:
if (SwingUtilities.isLeftMouseButton(...))
// do something
You could start by looking at How to write a Mouse Listener and the JavaDocs for MouseEvent in particular, the getButton method.
However, there are cross platform considerations that need to taken into consideration, which are overed by SwingUtilities.isLeftMouseButton and equivalent methods...
This will solve your problem
long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK;
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
public void eventDispatched(AWTEvent e) {
System.out.println(e.paramString()+"-"+e.getSource());
}
}, eventMask);
This is a Global Event Listeners.
Get the source and button from AWTEvent and do whatever you want to perform.
So I'm using a JPasswordField to get the user input, and then give instantaneous feedback regarding the strength of the entered password. My problem is that if the user clicks on the 'hide' checkbox, the string in the textfield isn't immediately masked by '●', but works only when the textfield regains focus. I've tried to use component.getFocus within the mouseListener, but it doesn't seem to work.
Here's what this particular listener looks like:
inputT.addMouseListener(new MouseAdapter(){
#Override
public void mouseClicked(MouseEvent hideClicked){
if (hideC.isSelected()){
inputT.setEchoChar('•');
inputT.requestFocus();
}
if (!hideC.isSelected()){
inputT.setEchoChar('\u0000');
inputT.requestFocus();
}
}
});
Show the feedback to the user in a different field than the password field that they are typing into. Ideally a separate read only component that is to the right of the password in your GUI. Your password strength feedback would then be updated by a listener on the password field, but the JPasswordField could then just work like normal.
From the JavaDoc: http://docs.oracle.com/javase/7/docs/api/javax/swing/JComponent.html#requestFocus%28%29
public void requestFocus()
Requests that this Component gets the input focus. Refer to Component.requestFocus() for a complete description of this method.
Note that the use of this method is discouraged because its behavior is platform dependent. Instead we recommend the use of requestFocusInWindow(). If you would like more information on focus, see How to Use the Focus Subsystem, a section in The Java Tutorial.
Also, as MadProgrammer pointed out - your listener is on the JPasswordField instead of the JCheckBox.
Don't use mouse listener. For doing on selection, use ItemListener:
checkBox.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent evt) {
if(evt.getStateChange()==ItemEvent.SELECTED)
jPasswordField1.setEchoChar((char)0);
else jPasswordField1.setEchoChar('*');
}
});
Well, the above code works perfectly for me.
Start by attaching a ActionListener to the the checkbox, this means that user can click or press the space bar to activate the check box
In the actionPerformed method, check the state of the checkbox and the required changes.
Use JPasswordField#selectAll or JPassword#requestFocusInWindow (usually I do it the other way round ;)). If these do not work, you might even consider using JPassword#repaint to force the field to repaint
I found similar code to the following on Oracle's website. I stripped some irrelevant stuff regarding layout for space reasons.
private JTextField textField;
public class TextDemo extends JPanel implements ActionListener
{
public TextDemo()
{
textField = new JTextField(5);
//This causes a leaking this in constructor warning...
textField.addActionListener(this);
//code here for layout and to add the textfield to the panel
}
private static int ctr = 0;
#Override
public void actionPerformed(ActionEvent evt)
{
System.out.println(ctr++);
}
}
So I made a print statement to print and increment a counter to check when this actionListener is detecting an action.
I was wondering:
Is the only action that triggers this method the enter button?
In my constructor where I attached this to the action listener of the textField object, what exactly happens?
An action event occurs, whenever an action is performed by the user.
Examples: When the user clicks a button, chooses a menu item, presses Enter in a text field. The result is that an actionPerformed message is sent to all action listeners that are registered on the relevant component.
this is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this
textField.addActionListener(this); // registering actionlistener
Capturing the action event
#Override
public void actionPerformed(ActionEvent evt)
{
System.out.println(ctr++); //perform some action on click
}
http://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
As suggested by Hovercraft Full Of Eels you can also use annonymous inner class as below
textField.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
//do something
}
});
AFAIK, yes. The listener is called when enter is pressed while the textfield has the focus.
The current object, being constructed, is added to the list of ActionListeners of the text field, so that when enter is pressed in the text field, the current object's actionPerformed() method is called. It's generally a bad practice to pass an object not being fully constructed to another object, because the other object could call it back directly and the object wouldn't be functional, since not fully constructed yet.
First: Typically one uses a DocumentListener for a JTextField. It tells lots more interesting information about what is going on. It actually ties to the document (a sort of Model) that lies behind the GUI field.
Now:
Q1 - It's hard to figure out which mouse and keyboard actions cause the various events on the Swing screen components. As I remember, may be it for ActionPerformed. But there can be custom code added to a sub-class of a JTextField that causes an action event to fire for anything you want. You DO have to be careful if you do this.
Q2: The Listener object is stored in a list of all the objects that want to know when the text field has that action event occur. When it occurs, the text field calls the actionPerformed() method on all the objects in its listener list.
You might want to do some research on the Observer Pattern. That is the a name often used for bits of code that do this sort of thing. It can be used in many situations. The key is that it loosely couples the listener and the listenee (observer and observed). The object listening only has to tell the object to which it is listening that it want to be notified in certain cases. The object that is being listened-to keeps a list of all the various objects listening and what events they want to be informed of. That's all the connection and with the use of simple interfaces, it can be programmed simply.
Is the only action that triggers this method the enter button?
Yes. For JTextFields an ActionEvent is dispatched by pressing ENTER.
In my constructor where I attached this to the action listener of the textField object, what exactly happens?
You register the ActionListener with the component. When an ActionEvent is triggered it dispatches an ActionEvent where actionPerformed is invoked, passing it the details of the source object in the ActionEvent.
The preferred approach for the implementation of ActionListeners is to use a separate anonymous listener per component or a single concrete Action for shared functionality.
In my code, two comboboxes are added to actionListener( this );
In another part of my code, I call a combobox function that sets an index to a certain value. This in turn calls actionPerfoemed again and so getSource == comboBox is true. Every time I call a set function it calls actionPerformed again, creating a stack of function calls that then unwinds down to the first.
Is there a way to prevent this?
If the problem is just the initial setting, you can defer adding the listener until after both have been initialized. There's more discussion here.
From the Swing tutorial,
Combo boxes also generate item events, which are fired when any of the items' selection state changes.
These events will be generated either when a user clicks on the items with the mouse, or when your software calls setSelectedIndex().
Perhaps you don't want your actionPerformed() method in this to be called when your software calls setSelectedIndex(). You may need a Boolean eventInitiatedBySoftware. In your main (this) class, you could say
synchronized(eventInitiatedBySoftware) {
eventInitiatedBySoftware=true;
comboboxeditor.setSelectedIndex(n);
}
and then in your listener:
public void actionPerformed(ActionEvent ae) {
synchronized(eventInitiatedBySoftware) {
if (eventInitiatedBySoftware) {
eventInitiatedBySoftware=false; // clear your flag.
return; // don't want to process this event.
}
// the rest of your method goes here
}
When your software wants to adjust the value, it will set the Boolean to true. The actionPerformed method will be called, but your test will realise that this event was initiated by the software, and return before doing any of your existing code. It will clear the Boolean, so that if a user now uses the mouse to perform a selection action, your code will realise that it wasn't softwareInitiated.
BTW, It's possible that you misunderstand the event concept. For example, I suspect you are actually adding "this" as an event listener for each combobox, rather than adding comboboxes as listeners to "this". You might like to look at the Writing Event Listeners trail.
Here's the situation, I have a jFrame with a tabbed pane and within the tabs I have a couple of jTables and a jTree. I want to be able to chain the selections between the tables and the tree based on whether a user uses a ctrl/shift + click versus a regular click. (If you hold ctrl and click in the first table/tree, it adds to the overall selection, if you use a regular click it clears the selections in the other tables/tree). Currently I'm having an issue with Java's jTree component. I have added a TreeSelectionListener and a MouseListener with a class that implements both interfaces, call it MyBigListener;
i.e.
MyBigListener listener = new MyBigListener();
jTree1.addMouseListener( listener );
jTree1.addTreeSelectionListener( listener );
MyBigListener implements TreeSelectionListener, MouseListener {
private boolean chained = false;
public synchronized setChained(boolean ch){
chained = ch;
}
public synchronized boolean isChained(){
return chained
}
public void valueChanged(TreeSelectionEvent e){
if(isChained()){ blah... }
}
public void mousePressed(MouseEvent e){
setChained(e.isControlDown() || e.isShiftDown());
}
}
My plan was to set a boolean flag if the user uses a ctrl/shift + click that I could check during the valueChanged(TreeSelectionEvent e) implemented by the tree selection listener.
I want to be able to process the mouse events before the valueChanged TreeSelectionEvents, but the problem is that I receive the mouse events after the valueChanged treeSelection event. This seems weird to me that I receive the selection change before the mouse pressed event fires, when the selection change is actually initiated by the mouse pressed. (I've already synchronized the boolean flag setting, which ironically helped to highlight the mis-order of events.)
I've already tried alternatives like adding a keyListener, but this doesn't work when the focus is on a separate frame, which goofs me up when a user holds ctrl and then clicks into the jTree causing it to receive both the focus and fire any valueChanged selection events.
Any help would be appreciated, thanks!
--EDIT-- #akf
I have separate jTables and jTrees in a tabbed Pane that serve as a summary/control panel for data in a nodal-graph. I'm using these components in the tabbed Pane to do coordinated selection to a graph displayed in a separate jFrame. Individually each table works just fine for its selection as does the jTree. It's coordinating between the panes that's tricky. This works fine so far with jTable components because I fire the new selections as the result of a MouseEvent where I can tell if the shift/ctrl button was down, formulate my new selection, and pass it to the parent frame which coordinates selections between all panes and sends the final selection to the graph. However, the parent needs to know if it needs to chain a selection between panes, or squash the others. With the jTables it's fine again, because I fire selection changes as the result of a mouse click. The jTree is more of a problem because I'm doing some forced selections. If you click on a branch, it forces all leaves into the selection. I needed to implement a TreeSelectionListener to do that, but only get a valueChanged(TreeSelectionEvent) to realized changes. I added a mouseListener to listen for ctrl+clicks and shift+clicks, but apparently the events don't always happen in the same order.. at least so far I receive the valueChanged event before the mousePressed event, so checking to if a ctrl+click happened posts after the selection has already been modified.
Right now, I'm posting a pending selection change and then have the MouseListener grab that and send it up the chain, but if these events aren't guaranteed to happen in the same order, at some point it's going to fail. Implementing a delayer also rubs me the wrong way.
Thanks for the help so far.
--EDIT2-- #ykaganovich
I think overriding the fireValueChanged method is closer to the right way to go about things. Depending on my definition of what actions should cause a "chained" selection to the other components, I'd need to gather some context on what's going on before the valuedChanged method fires. This basically means calling it myself in all cases where I can define what it means by who triggers it. I.e. If a mouse event causes it and ctrl is down then set what I need to set (interpret) then fire. If it's going to change due to a keyboard event, again, set what I need to set, then fire. I don't think the TreeSelectionModel is the way to go, because I still won't know what the circumstances were when the event fired. I think this means I'll need to rewrite parts of the jTree to do this but I guess I'll see how it goes. Thanks.
Don't do it that way, override JTree.fireValueChanged instead.
Try something like this (untested):
class ChainedSelectionEvent extends TreeSelectionEvent {
ChainedSelectionEvent(TreeSelectionEvent e) {
super(e.newSource, e.paths, e.areNew, e.oldLeadSelectionPath, e.newLeadSelectionPath);
}
}
protected void fireValueChanged(TreeSelectionEvent e) {
if(chained) { // figure out separately
super.fireValueChanged(new ChainedSelectionEvent(e));
} else {
super.fireValueChanged(e);
}
}
Then check instanceof ChainedSelectionEvent in your listener
EDIT
Actually, I think the right way to do this is to implement your own TreeSelectionModel, and override fireValueChanged there instead. Assuming setSelectionPath(s) methods imply a new selection, and add/removeSelectionPath(s) imply chaining, you could distinguish between the two cleanly. I don't like listening to either keyboard or mouse events explicitly, because there's more than one way to change a selection (e.g. if someone is holding down SHIFT and hitting a down-arrow, you won't get a mouse event).
You may get the mouse event before or after the tree selection event. Best not to rely on such orders. The reason is that the tree selection event is caused in response to the mouse event. Is that mouse event listener called before or after your listener? Could be either.
This sort of thing is closely involved with the implementation of the PL&F.
the key here is to understand that a JTree is delivered with a BasicTreeUI.MouseHandler, see javax.swing.plaf.basic.BasicTreeUI.
to answer the central question, "what fires fireValueChanged", the answer is "this mouse handler"... which turns out to be the only mouse listener returned when you go tree.getMouseListeners().
So you have to replace this default mouse listener with your own version ... which is a little tricky. I use Jython, which everyone needs to discover. This code shouldn't be too difficult to translate into Java however:
from javax.swing.plaf.basic import BasicTreeUI
def makeMouseHandlerClass():
class MouseHandlerClass( BasicTreeUI.MouseHandler ):
def mousePressed( self, mouseEvent ):
genLog.info( "mouse handler MOUSE PRESSED!" )
nTFSelf.mousePressedStatus = True
BasicTreeUI.MouseHandler.mousePressed( self, mouseEvent )
nTFSelf.mousePressedStatus = False
return MouseHandlerClass
suppliedMouseHandler = nTFSelf.taskTree.mouseListeners[ 0 ]
nTFSelf.taskTree.removeMouseListener( suppliedMouseHandler )
nTFSelf.taskTree.addMouseListener( makeMouseHandlerClass()( nTFSelf.taskTree.getUI() ))
... basically the equivalent Java here would be to extend BasicTreeUI.MouseHandler as MyMouseHandler, and then in the last line replace with new MyMouseHandler( tree.getUI() )... "nTFSelf" here is merely a reference to the "this" object of the code where all this is being written...