My Netbeans RCP application has a global key binding on the SPACE key which triggers action MyAction. It works fine.
My app contains an editable JSpinner. If user presses SPACE while he's editing the spinner value, it also triggers MyAction, and I don't want that.
I found a workaround with:
mySpinner.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke("SPACE"), "doNothing");
mySpinner.getActionMap().put("doNothing", new DoNothingAction());
But I will have other global key bindings in my app...
Is there a generic way to remove all key bindings in the JSpinner, without replicating the above code for each new key ?
Both InputMap and ActionMap have clear() methods that remove all existing bindings. You might not want to clear all the bindings though, because they include most probably arrow keys also which are used to select next previous values in spinner.
Related
I'm working on a text editor, and I want to be able to detect the difference between two distinct types of key events: the ones that result in a visible change to the text in my JTextArea (ie, alphanumeric characters, the enter key, symbols), and the ones that don't result in a visible change (directional keys, control keys, shortcuts). I know I can do this with a very verbose switch statement, but is there a less verbose way to do the following?
private void checkKey (java.awt.event.KeyEvent evt) {
if (saved && /*some way to check if the text in the box has changed */) {
editorTitle.setText(currentedit + " (Edited)");
saved = false;
}
}
Don't use a KeyListener. There are better API's to handle these situation.
If you want to know if the data in the text component has changed then you should be using a DocumentListener. It will generate an event whenever text is added or removed.
Read the section from the Swing tutorial on How to Write a DocumentListener for more information.
If you want to know when an arrow key is pressed to invoke some kind of Action then you should be using Key Bindings. This is how all Swing components work. A KeyStroke is bound to an Action.
See Key Bindings for more information and a complete list of the default key bindings of each component.
Shortcuts are also implemented in Swing by using Key Bindings.
I am trying to get my application to respond to keyboard input. Eventually, I would like it to register ctrl+f and initiate a search, but I started simple and tried for the space bar. The Java tutorials on using Key Bindings got me this far, but no matter what I apply the key binding to, nothing registers. In the below code, panel is a JPanel and the others are assorted swing objects which have been added to panel.
Action ctrlF = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("Action performed");
}
};
panel.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
openStallsList.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
openStalls.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
stallScroller.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
assignLabel.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
tenantInfo.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
unitSpinner.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
buildingAddress.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
buildingLogo.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),"crtlF");
What am I missing here? Does it have something to do with focus? There are a couple of assorted labels and buttons which are not included on that list. Is there any way to get panel to register all of the input from all of it's children?
Thanks
First, you need bind a KeyStroke to some kind of "key". Now personally, it's eaiser to specifiy the virtual key then using a String, as the String value can be a little temperamental, but that's me
panel.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),"crtlF");
Next, you need to bind the "key" to an Action
panel.getActionMap().put("crtlF", ctrlF);
See How to use key bindings for more details.
The next problem you will have is the component will need to be focused before the key binding can be triggered
You could try and get the InputMap with a different focus requirement using either WHEN_ANCESTOR_OF_FOCUSED_COMPONENT or WHEN_IN_FOCUSED_WINDOW which will allow you to change the focus level required by the component in order for the key binding to be triggered.
i.e.,
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = panel.getInputMap(condition);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0),"crtlF");
//... etc
I asked this question a little earlier, but maybe some of you were asleep, as where I live at the time it was probably very early in silicon valley.
My program has assigned new functions to the arrow keys, but for the very last part of the Swing display's presentation I need them to function normally within a TextArea, moing the cursor and such.
Is there a way to restore to defaults, or through an AbstractAction assign simple movement of the cursor once again?
The original answer I gave you showed how to replace the Action. If you need to restore the default Action then it is probably easier to create a new InputMap and ActionMap entry for the Action. The Key Bindings link I gave you shows how to do this.
Then when you need to restore the default Action you can use:
textField.getInputMap().put(keystroke, "none");
This will cause the original InputMap to be search again.
Another approach it to save the Action before you update the ActionMap with the custom Action. Something like:
Action original = textField.getActionMap().get(...);
Reread the Key Bindings link to better understand the usage of the InputMap and ActionMap.
I've a problem with setAccelerator(). Right now, I have the code that works for Ctrl+X for DELETE operation. I want to set the accelerator to Shift+Delete as well for same JMenuItem.
My code as follows:
JMenuItem item = new JMenuItem(menuText);
item.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_X, KeyEvent.CTRL_MASK));
item.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_DELETE, KeyEvent.SHIFT_MASK));
but this is working only for Shift+Delete operation. Seems it is overriding the Ctrl+X operation. Can we make both these keystrokes work at the same time?
Please guide.
Yes it can be done. Behind the scenes the setAccelerator() is just creating a Key Binding, however as you noticed the second binding replaces the first.
So, you need to create an Action (not an ActionListener) to add the to the menu item. Read the section from the Swing tutorial on How to Use Actions for more information. Now that you have created the Action, you can share the Action with another KeyStroke by manually creating a Key Binding. You can read the section from the Swing tutorial on How to Use Key Bindings for a detailed explanation. Or you can read my blog on Key Bindings which give some simple code examples.
This second binding will not show up on the menu item itself.
From: http://java.sun.com/j2se/1.4.2/docs/api/java/awt/AWTEvent.html
The masks are also used to specify to which types of events an AWTEventListener should listen.
So you can combine the mask for two keys, but not the KeyEvents.
item.setAccelerator(
KeyStroke.getKeyStroke(
KeyEvent.VK_X, KeyEvent.CTRL_MASK + KeyEvent.SHIFT_MASK));
A workaround solution would be to catch the KeyEvent in the middle (after your component fired it, but before your listeners will act on it) and check, whether its one of the two combinations. Then fire one event, on which you programmatically agree to represent the action you wanted.
The second call indeed overrides the accelerator. If the method starts with set, there will be only one. If the method starts with add, you can have more than one (for example for a number of listeners).
If you want multiple keystrokes to do the same, I think you should add a keyListener to the top frame (or panel, dialog, ...) which invokes the action listeners added to the menuItem.
I have a custom textfield class that extends the JTextField class in Swing.
I need to find a way to disable the default actions for Ctrl-A (select all), Ctrl-H (backspace) etc, so that the window containing the textfield can map these shortcuts to whatever it wants.
Any help would be greatly appreciated.
Okay, found the answer myself:
Added the following to an initilization method of the textfield class:
this.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK), "none");
The effect is that the textfield ignores the shortcut and lets the keystroke be passed along to the shortcut handler in the window.
How to make and remove key bindings would help you to implement.
To remove all of default key bindings, just dereference its parent InputMap.
jtextField.getInputMap().setParent(null);
But it remove all of key bindings so that you can't type any characters. JTextField's input has 3 parents. So you would be better with overriding specific key bindings as below.
InputMap inputMap = jtextfield.getInputMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Key_Event.CTRL_DOWN_MASK), "foo");
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, Key_Event.META_DOWN_MASK), "foo");
Maybe you should deal with the KeyMap.