I'm about to use keybinding in a swing application for the num pad enter key, but the key is difficult to catch.
All examples I have seen rely on something like
key == KeyEvent.VK_KP_LEFT
where VK_KP_LEFT is some predefined value. Other options are to define a keystoke like this:
KeyStroke.getKeyStroke("control A");
KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK);
but I have not found the "modifier" for numpad.
What is easily to obtain is the difference between the general and the numpad-enter:
All numpad-keys (indepently if switched in the numeric mode or not) get assigned the
getKeyLocation() == 4
(I spottetd this from key pressed / key released methods)
The question is:
How to properly prepare the keyStroke for numpad enter key to use it in the
inputMap.put(KeyStroke keyStroke, Object actionMapKey)
key binding method?
Thanks,
Tarik
If you're looking for binding Enter key you can use KeyEvent.VK_ENTER, ie:
getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "someAction");
getActionMap().put("someAction", someAction);
Here is a short example:
import java.awt.event.*;
import javax.swing.*;
public class Test {
public static void main(String[] args) throws Exception {
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
panel.add(new JLabel("Hit Enter"));
Action someAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(frame, "Got it");
}
};
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "someAction");
panel.getActionMap().put("someAction", someAction);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
EDIT: VK_ENTER on numeric pad vs main keyboard
After some testing, it looks like it may not be possible to bind these keys separately.
The same KeyStroke is generated for both keys. The implementation of JComponent.processKeyBinding does not examine the KeyEvent, all it cares is KeyStroke in order to find the desired action.
SwingUtilities.notifyAction that is responsible for dispatching the actual action does not delegate all the details of the KeyEvent (only key, modifiers and when). So inside action there is no way to distinguish either as there is no details in ActionEvent.
If it worth the trouble, you can override processKeyBinding and add some logic if needed. You can also use KeyboardFocusManageraddKeyEventDispatcher() for blocking one of the keys.
How about this?
if(keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD
&& keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
Related
I'm just trying to learn how the key binders work, and it seems I've misunderstood something from the Java tutorials. This is the code:
public class KeyBinder {
public static void main(String[] args) {
//making frame and label to update when "g" key is pressed.
JLabel keybinderTestLabel;
JFrame mainFrame = new JFrame();
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(300,75);
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
keybinderTestLabel = new JLabel("Press the 'g' key to test the key binder.");
mainFrame.add(keybinderTestLabel);
Action gPressed = new AbstractAction(){
#Override
public void actionPerformed(ActionEvent e) {
keybinderTestLabel.setText("Key Binding Successful.");
System.out.println("Key Binding Successful.");
//Testing to see if the key binding was successful.
}
};
keybinderTestLabel.getInputMap().put(KeyStroke.getKeyStroke("g"), "gPressed");
keybinderTestLabel.getActionMap().put("gPressed", gPressed);
/*
* from my understanding, these two lines map the KeyStroke event of the g key
* to the action name "gpressed", then map the action name "gpressed" to the action
* gpressed.
*
*/
}
}
From what I understand, I mapped the g keystroke to the action name "gPressed", then mapped that to the action gPressed. For some reason though, when I run the program, pressing the g key does not update the text label. What's the issue here? is the "g" keystroke not actually mapped to the g key on the keyboard?
So, from the JavaDocs
public final InputMap getInputMap()
Returns the InputMap that is used
when the component has focus. This is convenience method for
getInputMap(WHEN_FOCUSED).
Since JLabel is not focusable, this will never work, instead, you need to supply a different focus condition, for example...
keybinderTestLabel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW). //...
Also, and this is a personal preference... KeyStroke.getKeyStroke("g") using KeyStroke.getKeyStroke like this can be problematic as the String you supply needs be very precise in it's meaning, and I can never remember exactly how it should work (and it's not overly documented).
If the first suggestion fails to fix the issue, also try using KeyStroke.getKeyStroke(KeyEvent.VK_G, 0) instead
I have editable JCombobox and I added keylistener for combobox editor component.
When user press 'Enter key' and if there is no text on the editable combobox I need to display message box using JOptinoPane. I have done necessary code in keyrelease event and it displays message as expected.
Problem is, when we get message box and if user press enter key on 'OK' button of JOptionPane, combobox editor keyevent fires again. Because of this, when user press Enter key on message box, JoptionPane displays continuously.
Any idea how to solve this?
Note that I can't use Action listener for this.
Please check if this code helps you!!!
JFrame frame = new JFrame("Welcome!!");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComboBox cmb = new JComboBox();
cmb.setEditable(true);
cmb.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
#Override
public void keyReleased(KeyEvent event) {
if (event.getKeyChar() == KeyEvent.VK_ENTER) {
if (((JTextComponent) ((JComboBox) ((Component) event
.getSource()).getParent()).getEditor()
.getEditorComponent()).getText().isEmpty())
System.out.println("please dont make me blank");
}
}
});
frame.add(cmb);
frame.setLocationRelativeTo(null);
frame.setSize(300, 50);
frame.setVisible(true);
Most people find it difficult because of this casting.
We need to add a key listener on the component that the combo box is using to service the editing.
JTextComponent editor = (JTextComponent) urCombo.getEditor().getEditorComponent();
editor.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent evt) {
// your code
}
});
Hope this code helps.
Note that I can't use Action listener for this.
this doesn't make me any sence, then to use ItemListener
Any idea how to solve this?
never to use KeyListener for Swing JComponents, use (Note that I can't use Action listener for this.) KeyBindings instead,
notice ENTER key is implemented for JComboBox in API by default, have to override this action from ENTER key pressed
One option would be to replace the KeySelectionManager interface with your own. You want to replace the JComboBox.KeySelectionManager as it is responsible for taking the inputted char and returns the row number (as an int) which should be selected.
Please check the event ascii code by ev.getkeycode() and check if it is a number or character. If it is neither a number nor a character do nothing.
If it is what you want then do the process.
If you are using Netbeans then right click on your combobox and select customize code.
add following lines of code
JTextComponent editor = (JTextComponent) Code.getEditor().getEditorComponent();
editor.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent evt) {
if(evt.getKeyCode()==10)
//do your coding here.
}
});
The standard key combination for help is command-? on macs. How can I bind this key combination to a menu item.
Note: As our users have different keyboard layouts I´m looking for a solution that does not require knowledge about what key "?" is located on.
Using KeyStroke.getKeyStroke(String), which javadoc says;
Parses a string and returns a `KeyStroke`. The string must have the following syntax:
<modifiers>* (<typedID> | <pressedReleasedID>)
modifiers := shift | control | ctrl | meta | alt | button1 | button2 | button3
typedID := typed <typedKey>
typedKey := string of length 1 giving Unicode character.
pressedReleasedID := (pressed | released) key
key := KeyEvent key code name, i.e. the name following "VK_".
I have this example code:
import javax.swing.*;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
public class HelpShortcut extends JFrame {
public HelpShortcut(){
// A few keystrokes to experiment with
//KeyStroke keyStroke = KeyStroke.getKeyStroke("pressed A"); // A simple reference - Works
//KeyStroke keyStroke = KeyStroke.getKeyStroke("typed ?"); // Works
KeyStroke keyStroke = KeyStroke.getKeyStroke("meta typed ?"); // What we want - Does not work
// If we provide an invalid keystroke we get a null back - fail fast
if (keyStroke==null) throw new RuntimeException("Invalid keystroke");
// Create a simple menuItem linked to our action with the keystroke as accelerator
JMenuItem helpMenuItem = new JMenuItem(new HelpAction());
helpMenuItem.setAccelerator(keyStroke);
// Install the menubar with a help menu
JMenuBar mainMenu = new JMenuBar();
JMenu helpMenu = new JMenu("Help");
helpMenu.add(helpMenuItem);
mainMenu.add(helpMenu);
setJMenuBar(mainMenu);
}
// Scaffolding
public static void main(String[] pArgs) {
HelpShortcut helpShortcut= new HelpShortcut();
helpShortcut.setLocationRelativeTo(null);
helpShortcut.setSize(new Dimension(100, 162));
helpShortcut.setVisible(true);
}
private class HelpAction extends AbstractAction {
public HelpAction() {
putValue(Action.NAME,"Help me!");
}
#Override
public void actionPerformed(final ActionEvent pActionEvent) {
JOptionPane.showMessageDialog(HelpShortcut.this,"You should ask StackOverflow!");
}
}
}
On my keyboard the "?" is above the "/" key so you also next to use the shift key to type the "?". So to do the binding you need to use:
// KeyStroke keyStroke = KeyStroke.getKeyStroke("meta typed ?");
int modifier = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
+ KeyEvent.SHIFT_DOWN_MASK;
KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, modifier);
See KeyEvent API doc - notes section:
Not all characters have a keycode associated with them. For example, there is no keycode for the question mark because there is no keyboard for which it appears on the primary layer.
(surprisingly - to me :-) modifiers to bindings of keyIDs "typed" are not supported: while you can create and bind such into the inputMap, they are never found because keyStrokes internally generated for typed keyEvents do use the keyChar and ignore the modifiers. That creation happens in JComponent.processKeyBindings(...)
boolean processKeyBindings(KeyEvent e, boolean pressed) {
if (!SwingUtilities.isValidKeyEventForKeyBindings(e)) {
return false;
}
// Get the KeyStroke
KeyStroke ks;
if (e.getID() == KeyEvent.KEY_TYPED) {
ks = KeyStroke.getKeyStroke(e.getKeyChar());
}
else {
ks = KeyStroke.getKeyStroke(e.getKeyCode(),e.getModifiers(),
(pressed ? false:true));
}
Thinking about it, that may make sense: pressed/released handles the physical keys, while typed is the final combined "output" of one or more physical keys. If there is no valid keyChar for any given combination, there is no keyTyped event generated.
The base problem is the well-known usa centrism of the swing/awt developers: they counted as physical keys only those on the us layout ;-) No way (that I know of) to get at other keys in a layout-agnostic manner. Hope to be proven wrong
I'm making an application with java that has a JTextPane. I want to be able to execute some code when the enter key is pressed (or when the user goes to the next line). I've looked on the web and not found a solution. Would it be better to tackle this with C#? If not, how can i register the Enter key in the JTextPane's keyTyped() event? If C# is a good option, how would i do this in C#?
Here is a solution i thought would work...but did not
//Event triggered when a key is typed
private void keyTyped(java.awt.event.KeyEvent evt) {
int key = evt.getKeyCode();
if (key == KeyEvent.VK_ENTER) {
Toolkit.getDefaultToolkit().beep();
System.out.println("ENTER pressed");
}
}
Why the above example does not work is because no matter which key i press, i get a keyCode of 0. I would prefer a solution to this problem in Java but C# would work just as well, maybe better. Also, please try to answer the question with examples and not links(unless you really need to). Thanks!
One solution is to add a key binding on the textpane. e.g.,
JTextPane textPane = new JTextPane();
int condition = JComponent.WHEN_FOCUSED;
InputMap iMap = textPane.getInputMap(condition);
ActionMap aMap = textPane.getActionMap();
String enter = "enter";
iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), enter);
aMap.put(enter, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent arg0) {
System.out.println("enter pressed");
}
});
This answer is in case anyone ever views this thread I got the same things as Mr. Mohammad Adib.
So instead of using ...
(evt.getKeyCode()==evt.VK_ENTER)
I used ...
(evt.getKeyChar()=='\n')
and the solution worked.
I am looking for ENTER key in the password text field, to launch the login method when ENTER was pressed. The code below will print in the console the keycode. After running the program and typing a few tihngs in the box I discovered for ENTER key it is code 13.
txtPass = new Text(shlLogin, SWT.BORDER | SWT.PASSWORD);
txtPass.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
System.out.println(e.keyCode);
if (e.keyCode == 13) { /* ... Do your stuff ... */ }
}
});
If you are looking for a single key press, you can still be a little lazy and avoid learning new stuff about key bindings, by using this method. The fun begins when adding CTRL+[Letter] shortcuts - but this is for another discussion.
We are replacing a legacy C application originally written for MSDOS (yes, believe it or not!). This application uses a specially remapped keyboard which intercepts the DOS keyboard interrupt (remember that??!) to sometimes alter the scan codes of the keys pressed by the user so that different processing would occur. Special labels were then placed on the keys telling the users the "new" meaning of these keys.
The new Java version is required to preserve this keyboard layout which the targeted group of users is very familiar with.
An example of what we are trying to do is as follows:
You may never have thought about this, but the numeric keypad of a modern telephone is reversed from the numeric keypad of a computer keyboard. On the former 1-2-3 is on the top row and on the latter it is on the bottom row. We are required to make the keyboard's numeric keypad look like the telephone. Let's say, when the user types "7" on the numeric keypad, we want it look as though he typed a "1", when he types an "8", we want a "2", when he types a "3" we want a "9".
There is much more that we have to do to emulate the DOS application, but we can't even solve this simple case now. I have been all over Key Binding, KeyAdapters, KeyListeners, and even KeyEventDispatchers, and I cannot make this work. I am pretty sure we have to work on the lowest level we are allowed to work by Java to come as close as possible to what the legacy app does. And needless to say, we want the cleanest implementation possible, so that the application level code is not littered with inputMaps and actionMaps etc. As much as possible this needs to be handled globally. Can anyone help?
If I were doing this, I would write the Java App without worrying about the key bindings. Assume that when a component gets a keyevent for #7 its #7, don't worry about whether the 7 or 1 was really typed. The app shouldn't care about how keys are mapped on the keyboard. This should let you start developing the app immediately.
As far as overriding key bindings, this seems like where you want to look: http://download.oracle.com/javase/7/docs/api/java/awt/KeyEventDispatcher.html
It sounds like you can write your own KeyEventDispatcher to handle all the key mapping logic and it prevents mapping logic from messing up the rest of the logic in your application.
This is hacky, and I'll admit I haven't used it myself, but you could subclass KeyEvent and override the fields as needed. So yourSubclass.VK_NUMPAD1 is the integer value of KeyEvent.VK_NUMPAD7, yourSubclass.VK_NUMPAD2 is the integer value of KeyEvent.VK_NUMPAD8, etcetera. Then use your subclass everywhere KeyEvent is normally used.
I also agree that the KeyEventDispatcher should work. But if this a version/platform issue, then maybe you can use a custom Event Queue. See Global Event Dispatching
My stack overfloweth!
The answer was here:
http://download.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html
which says:
For key pressed and key released
events, the getKeyCode method returns
the event's keyCode. For key typed
events, the getKeyCode method always
returns VK_UNDEFINED.
My original attempt thought it could get a keyCode on KEY_TYPED. It could not, and it was the KEY_TYPED event that was clobbering the mapping done in KEY_PRESSED.
Here is a working implementation:
import static java.awt.event.KeyEvent.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.KeyEventDispatcher;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class KeyboardDispatcherDemo extends JFrame {
/**
* This class shows how to map numeric keypad keys.
* It performs two conversions:
* 1. lower-to-uppercase
* 2. if numeric keypad 7-9 is typed, 1-3 appears and vice versa.
*
* This is modified from the code at
* http://www.exampledepot.com/egs/java.awt/DispatchKey.html#comment-51807
* which demoes the lower-to-upper conversion.
*
* It doesn't yet handle modified numeric keypad keys.
*
*/
public KeyboardDispatcherDemo() {
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
private char lastMappedKey;
private final Map<Integer, Character> keyMap =
new HashMap<Integer, Character>() {
{
put(VK_NUMPAD1, '7');
put(VK_NUMPAD2, '8');
put(VK_NUMPAD3, '9');
put(VK_NUMPAD7, '1');
put(VK_NUMPAD8, '2');
put(VK_NUMPAD9, '3');
}};
public boolean dispatchKeyEvent(KeyEvent e) {
System.out.println(String.format("INPUT: %s", e.toString()));
boolean dispatch = false;
switch (e.getID()) {
case KeyEvent.KEY_PRESSED:
dispatch = dispatchKeyPressed(e);
break;
case KeyEvent.KEY_TYPED:
dispatch = dispatchKeyTyped(e);
break;
case KeyEvent.KEY_RELEASED:
dispatch = dispatchKeyReleased(e);
break;
default:
throw new IllegalArgumentException();
}
System.out.println(String.format("OUTPUT: %s", e.toString()));
System.out.println();
return dispatch;
}
private boolean dispatchKeyPressed(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
if (Character.isLetter(k)) {
e.setKeyChar(Character.toUpperCase(e.getKeyChar()));
} else if (e.getModifiers() == 0){
Character mapping = keyMap.get(e.getKeyCode());
if (mapping != null) {
e.setKeyChar(mapping);
}
}
// save the last mapping so that KEY_TYPED can use it.
// note we don't do this for modifier keys.
this.lastMappedKey = e.getKeyChar();
}
return false;
}
// KEY_TYPED events don't have keyCodes so we rely on the
// lastMappedKey that was saved on KeyPressed.
private boolean dispatchKeyTyped(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
e.setKeyChar(lastMappedKey);
}
return false;
}
private boolean dispatchKeyReleased(KeyEvent e) {
char k = e.getKeyChar();
if (k != CHAR_UNDEFINED) {
e.setKeyChar(lastMappedKey);
this.lastMappedKey=CHAR_UNDEFINED;
}
return false;
}
});
setTitle("KeyboardDispatcherDemo");
JPanel panel = new JPanel();
panel.setBackground(new Color(204, 153, 255));
panel.setLayout(new BorderLayout());
getContentPane().add(panel, BorderLayout.CENTER);
JTextArea staticText = new JTextArea();
staticText.setText("This demonstrates how to map numeric keypad keys. It uppercases all letters and converts Numeric Keypad 1-3 to 7-9 and vice versa. Try it.");
staticText.setLineWrap(true);
staticText.setWrapStyleWord(true);
panel.add(staticText, BorderLayout.NORTH);
staticText.setFocusable(false);
JTextField textField = new JTextField();
textField.setText("");
textField.setHorizontalAlignment(SwingConstants.LEFT);
panel.add(textField, BorderLayout.SOUTH);
textField.setColumns(10);
textField.setFocusable(true);
setSize(getPreferredSize());
setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
new KeyboardDispatcherDemo();
}
#Override
public Dimension getPreferredSize() {
// TODO Auto-generated method stub
return new Dimension(400,300);
}
}
Thanks to all who nudged me toward the answer.
which brings me to my next question ... stay tuned.