21

How can I let my custom KeyListener listen for combinations of ALT (or CTRL for that matter) + more than one other key?

Assume I have 11 different actions I want the application to do, depending on a combination of keys pressed. ALT + 0 - ALT + 9 obviously don't pose any problems, whereas for ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar) I cannot find a good solution anywhere on the web (or in my head). I'm not convinced that this solution with a timer is the only possible way.

Thanks a million in advance for any suggestions!

Edit: Actions 0-9 + action 10 = 11 actions. Thanks @X-Zero.

s.d
  • 4,017
  • 5
  • 35
  • 65
  • `ALT10` would actually be the 11th action... I'd also usually try to avoid something that _requires_ **two** hands to press (because 1 and 0 are usually at opposite ends of the keyboard) - try using a different/additional modifier key instead. Also, if you attempt to add another hotkey, you couldn't use `ALT11`... – Clockwork-Muse Oct 21 '11 at 16:51
  • @X-Zero: Sorry, you're right, it would be the 11th action :). WHY I want to do this though is because the actions will be called on columns in a table which have `int`s for headings. So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press `ALT` + `1` + [lets go of both `ALT` and `1`] `0`. Instead of using the `ALT` key I can imagine using another key as well such as `Shift` or similar. – s.d Oct 21 '11 at 18:40
  • 1
    You'd be better off assigning a hotkey that brings up a column-selection dialog, that the user can enter column index or name into (names are better than index-only). This should be _far_ easier to implement, and _much_ easier on the user. Besides which, how would you be able to tell that the user wanted to change column 11, versus putting a 1 in column 1? And don't display columns numbers starting with 0, as that will only cause confusion for people unused to 0-based arrays. – Clockwork-Muse Oct 21 '11 at 19:43
  • For an example of how users will likely expect release or multi-step hotkeys to work, look at how Eclipse nests dialogs. Specifically, key-release pops up the initial dialog, and further keypresses activate objects on that dialog. You users will likely expect this behaviour, as opposed to shifting into a 'tens-based' hotkey approach (and what happens when you get to the 100th column?). Also, be prepared for when, despite the usefulness of your hotkeys, you users never find or (potentially refuse to) adopt the hotkeys. – Clockwork-Muse Oct 21 '11 at 19:47
  • Hi @X-Zero: The application has a highly specific user base which is used to working with arrays (in fact they know they are working on one), hence I don't think the column no.s starting with 0 are an issue. I want to circumvent a popup dialog which triggers another popup dialog, and as speed is of essence, the user should be able to type in `ALT + 1 + 1` (or `ALT + 1 + 1 + 1` for that matter) and then the popup appears to update data in another row in the table. The `ALT + X` key combination is reserved for "calling columns" in the application, so I don't need to fear "cross-usage". – s.d Oct 24 '11 at 09:17
  • @X-Zero cont.: Will look at the Eclipse nested dialogs (I expect I can find them in the source code for, e.g., menus?), thanks for the tip. If implementing these hotkeys proves a greater issue than I thought though, I will surely "risk" an additional key press in there to show a simple col-selection dialogue. Thanks! – s.d Oct 24 '11 at 09:20

6 Answers6

31

You should not use KeyListener for this type of interaction. Instead use key bindings, which you can read about in the Java Tutorial. Then you can use the InputEvent mask to represent when the various modifier keys are depresed. For example:

// Component that you want listening to your key
JComponent component = ...;
component.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
                            java.awt.event.InputEvent.CTRL_DOWN_MASK),
                    "actionMapKey");
component.getActionMap().put("actionMapKey",
                     someAction);

See the javadoc for KeyStroke for the different codes you can use while getting the KeyStroke. These modifiers can be OR'ed together to represent various combinations of keys. Such as

KeyStroke.getKeyStroke(KeyEvent.VK_SPACE,
                       java.awt.event.InputEvent.CTRL_DOWN_MASK
                       | java.awt.event.InputEvent.SHIFT_DOWN_MASK)

To represent when the Ctrl + Shift keys were depressed.

Edit: As has been pointed out, this does not answer you question but instead should just be taken as some good advice.

Jonathan Spooner
  • 7,682
  • 2
  • 34
  • 41
  • 3
    I agree 100% with your recommending key bindings and have up-voted your answer because of this, but this answer doesn't address his main issue: capturing alt + two different key presses combined. – Hovercraft Full Of Eels Oct 21 '11 at 15:24
  • You are completely correct. I did not read the question correctly. – Jonathan Spooner Oct 21 '11 at 15:26
  • Thanks @Jonathan Spooner for recommending key bindings anyway. This makes a lot of sense, especially since it solves the focus problem a lot of people usually have with `KeyListener`s. – s.d Oct 21 '11 at 15:30
  • 2nd. Keybindings per one day +1, maybe I miss there `JComponent.WHEN_IN_FOCUSED_WINDOW` – mKorbel Oct 21 '11 at 17:05
  • @mKorbel: Sorry, I don't quite get your comment. Please specify what you mean? – s.d Oct 24 '11 at 09:09
  • @baphomet13 that's to (Jonathan Spooner) – mKorbel Oct 24 '11 at 09:37
  • I've accepted this answer, as it points towards what I should use instead of `KeyListener`, i.e. key bindings. – s.d Nov 01 '11 at 13:51
  • 1
    *"As has been pointed out, this does not answer you question but instead should just be taken as some good advice."* See [Is “Don't do it” a valid answer?](http://meta.stackexchange.com/questions/8891/is-dont-do-it-a-valid-answer) Summary: Heck yes! – Andrew Thompson Feb 08 '15 at 04:07
21

You can use KeyListener for this purpose by combining certain things. Look at the following example, which should be placed in an overridden keyPressed(KeyEvent e) method.

if (e.isControlDown() && e.getKeyChar() != 'a' && e.getKeyCode() == 65) {
        System.out.println("Select All"); 
}

The string Select All will be displayed when Ctrl + a is pressed. The method e.isControlDown() checks whether the Ctrl key is pressed or not. Similarly, the Alt key combinations can be done with the same method by using e.isAltDown() method.

The logic behind this is, e.getKeyChar() returns the character of the key presses & e.getKeyCode() returns its ASCII code. When Ctrl is pressed and hold, the e.getKeyChar() won't return a and e.getKeyCode() will be the same 65. Hope you understand this. Feel free to ask more.

s.d
  • 4,017
  • 5
  • 35
  • 65
Sasivarnan
  • 532
  • 6
  • 11
2

ALT + 1 + 0 (or "ALT+10" as it could be described in a Help file or similar)

seems to clash with (from one of your comments):

So for example if the user wants to change data in column 11 (which would be called "10"), s/he'd press ALT + 1 + [lets go of both ALT and 1] 0.

Assuming that ALT+10 means 'Pressing ALT, pressing and releasing 1, pressing and releasing 0, releasing ALT' I propose trying this:

In keyPressed, listening for the ALT key, activate a boolean flag, isAltPressed, and create a buffer to hold key presses that occur (a string, say). In keyTyped, if isAltPressed is active, append key codes to the buffer. In keyReleased, listening for ALT again, open a conditional querying the buffer and executing actions.

    public void keyPressed (KeyEvent e){
        if (e.getKeyCode() == KeyEvent.VK_ALT){
        buffer = ""; //declared globally
        isAltPressed = true; } //declared globally
    }

    public void keyTyped (KeyEvent e){
        if (isAltPressed)
            buffer.append (e.getKeyChar());
    }

    public void keyReleased (KeyEvent e){
        if (e.getKeyCode() == KeyEvent.VK_ALT){
            isAltPressed = false;
            if (buffer.equals (4948)) //for pressing "1" and then "0"
                doAction();
            else if (buffer.equals(...))
                doOtherAction();
            ...
        }//if alt
    }
sideways8
  • 153
  • 1
  • 1
  • 14
0
import java.awt.*;
import java.awt.event.*;
class KDemo
{
     public static void main(String args[])
     {
           Frame f = new Frame();
           f.setSize(500,500);
           f.setVisible(true);
           f.addKeyListener(new KeyAdapter()
           {
               public void keyPressed(KeyEvent e)
               {
                   AWTKeyStroke ak = AWTKeyStroke.getAWTKeyStrokeForEvent(e);
                   if(ak.equals(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_F4,InputEvent.ALT_MASK)))
                   {
                     System.exit(0);
                   }
               }
           });
        }
   }
Jaggler3
  • 304
  • 4
  • 10
0

I would suggest that instead of using key combinations, consider some input field when the window isVisible() and is focused. The field can be hidden, like Windows' File explorer hidden filename search (enter a directory, type the filename and the correspondence is focused), or is visible, like in Ubuntu.

Key combinations are not designed for including more than one key other than modifiers, although you may be able to achieve this.

WesternGun
  • 11,303
  • 6
  • 88
  • 157
0

I think there's a simpler way which I am using. If the KeyEvent is ev then if you investigate:

(int)ev.getKeyChar()

you find that ctrl-a is 1, ctrl-b is 2 and so on. I wanted to use ctrl-s for save. So I just use:

(((int)ev.getKeyChar())==19)

to detect it. No idea why, but it works fine whereas:

ev.isControlDown() && ev.getKeyChar()=='s'

does not.

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
dave110022
  • 64
  • 5