How do I make my JDialog's buttons respond to enter key?

Discussion in 'Java' started by Paul Tomblin, Oct 21, 2004.

  1. Paul Tomblin

    Paul Tomblin Guest

    How do I make JDialog buttons work like the buttons in JOptionPane, where
    pressing the enter/return key does the action of the selected button? At
    first I thought I was my code, so I downloaded the JDialog code from two
    Swing books, and both of them have the same problem - it highlights the
    buttons as you tab around, but hitting return does nothing.

    I've tried this on both Mac OS X and Linux, Java 1.4.2.
     
    Paul Tomblin, Oct 21, 2004
    #1
    1. Advertisements

  2. I haven't played with that in a while, but I think it was
    public void setMnemonic(int) in javax.swing.AbstractButton that will do it.
    The argument is one of the VK_* consts defined in KeyEvent.
    --Paul Bilnoski
     
    Paul Bilnoski, Oct 22, 2004
    #2
    1. Advertisements

  3. Paul Tomblin

    VisionSet Guest


    Action.putValue(Action.ACCELERATOR_KEY, KeyEvent.VK_ENTER)

    Mnemonics are for gaining focus on a control not operating them.
     
    VisionSet, Oct 22, 2004
    #3
  4. Paul Tomblin

    Bryan Cooper Guest

    I wrote a utility class that maps actions to key events. Here's the class:


    import java.awt.Component;
    import java.awt.DefaultKeyboardFocusManager;
    import java.awt.KeyEventDispatcher;
    import java.awt.KeyEventPostProcessor;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.util.HashMap;
    import java.util.Set;

    import javax.swing.Action;

    import org.apache.log4j.Logger;

    /**
    * This class handles the default key strokes the users type
    * in a panel or container by executing the registered actions. If no
    * actions are registered, then nothing will happen. Any key
    * stroke can be mapped to an action, however, the two main
    * key strokes mapped in this object are the \<enter\> and
    * \<escape\> keys. The key is a KeyEvent.VK_<key> static
    * value and the action is what will be executed if
    * the keystroke is typed by the user.
    * @author B. Cooper
    * @since v1.0.0
    */
    public class DefaultKeyHandler
    extends DefaultKeyboardFocusManager
    implements KeyListener, KeyEventDispatcher, KeyEventPostProcessor {
    /** The table that will hold the actions to execute */
    private HashMap actionMap = new HashMap();
    /** The logger object */
    private Logger logger = Logger.getLogger( DefaultKeyHandler.class);

    /**
    * Default Constructor
    */
    public DefaultKeyHandler() {
    super();
    }

    /**
    * @see
    java.awt.KeyEventDispatcher#dispatchKeyEvent(java.awt.event.KeyEvent)
    */
    public boolean dispatchKeyEvent(KeyEvent e) {
    /** Add custom code here if you need to... */
    return false;
    }

    /**
    * If a KeyEvent matches the key in the actionMap object,
    * the associated action will be fired.
    * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
    */
    public void keyPressed(KeyEvent e) {
    int keyCode = e.getKeyCode();
    Set keys = actionMap.keySet();
    Integer findKey = new Integer(keyCode);
    if (keys.contains(findKey)) {

    final Action a = (Action)actionMap.get(findKey);
    try {
    a.actionPerformed(
    new ActionEvent(
    this,
    1,
    (String)a.getValue(Action.ACTION_COMMAND_KEY)));
    } catch (Exception ex) {
    logger.error("Exception on ketPressed action!", ex);
    }
    }
    }

    /**
    * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
    */
    public void keyReleased(KeyEvent e) {
    }

    /**
    * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
    */
    public void keyTyped(KeyEvent e) {
    }

    /**
    * This method will map an action to the enter key event
    * @param action
    */
    public void mapEnterKeyAction(Action action) {
    actionMap.put(new Integer(KeyEvent.VK_ENTER), action);
    }

    /**
    * This method will map an action to the escape key event
    * @param action
    */
    public void mapEscapeKeyAction(Action action) {
    actionMap.put(new Integer(KeyEvent.VK_ESCAPE), action);
    }

    /**
    * This method will map an action to the escape key event
    * @param keyEventId
    * @param action
    */
    public void mapKeyAction(int keyEventId, Action action) {
    actionMap.put(new Integer(keyEventId), action);
    }

    /**
    * @see
    java.awt.KeyEventPostProcessor#postProcessKeyEvent(java.awt.event.KeyEvent)
    */
    public boolean postProcessKeyEvent(KeyEvent e) {
    /** Add custom code here if you need to... */
    return false;
    }

    /**
    * @see
    java.awt.KeyEventPostProcessor#postProcessKeyEvent(java.awt.event.KeyEvent)
    */
    public void processKeyEvent(Component component, KeyEvent e) {
    /** Add custom code here if you need to... */
    }

    /**
    * This method registers a component for the mapped actions.
    * @param component
    */
    public void registerComponent(Component component) {
    component.addKeyListener(this);
    if (component instanceof Container) {
    Component[] comps = ((Container)component).getComponents();
    for (int i = 0; i < comps.length; i++) {
    registerComponent((comps);
    }
    }
    }
    }

    Here's how you would use it:

    public MyPanel extends JPanel {

    private DefaultKeyHandler keyHandler = new DefaultKeyHandler();
    ....
    public MyPanel() {
    keyHandler.mapEnterKeyAction(new YourOKButtonAction());
    keyHandler.mapEscapeKeyAction(new YourCancelButtonAction());
    keyHandler.registerComponent(this);
    }
    ....
    }

    The registerComponent method will register the panel and all of it's
    contained components with the actions that you defined.

    Hope this helps out.

    Bryan Cooper
     
    Bryan Cooper, Oct 22, 2004
    #4
  5. Paul Tomblin

    Paul Tomblin Guest

    Didn't work. I changed all the ActionListeners on the buttons to
    AbstractActions, then did a
    buttonAction.putValue(Action.ACCELERATOR_KEY,
    new Integer(KeyEvent.VK_ENTER));
    button.addAction(buttonAction);
    on each one, but it didn't help.

    Oh well, I'll keep working at it.
     
    Paul Tomblin, Oct 22, 2004
    #5
  6. (Paul Tomblin) wrote in @allhats.xcski.com:
    It's not clear to me exactly what you are asking.

    In case what you are asking about is how to make a default button work with
    ENTER, have a look at:

    JComponent.getRootPane().setDefaultButton()

    Chas Douglass
     
    Chas Douglass, Oct 22, 2004
    #6
  7. Paul Tomblin

    Paul Tomblin Guest

    It's simple. If I tab between the buttons in either a JOptionPane or a
    JDialog, the buttons get a highlight (unless you call
    "setFocusPainted(false)"). But if I hit return in a JOptionPane, the
    highlighted button's action gets performed just as if I'd clicked on it.
    In a JDialog, if I hit return, nothing happens. I would like it to behave
    like a JOptionPane. Actually, I would like all my java dialogs to respond
    to the enter key - radio and toggle buttons toggling, combo boxes opening
    up, etc. Native guis do this by default.

    Interesting. That makes *a* button respond to the enter key, but it
    doesn't have anything to do with where the focus is.
     
    Paul Tomblin, Oct 22, 2004
    #7
  8. (Paul Tomblin) wrote in
    So, it's not quite so simple...
    I suggest you try it. Based on the way it works in my program (that is
    to say, I have NOT looked at the code for the way JOptionPane is
    implemented) I beleive it is how JOptionPane gets its functionality.

    But since you were really asking for more than the way JOptionPane works,
    you should look at JComponent.getInputMap() and adding custom actions.

    Also:

    http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/doc-files/Key-
    Windows.html#JTable
    shows what all the default mappings are.

    Chas Douglass
     
    Chas Douglass, Oct 22, 2004
    #8
  9. Paul Tomblin

    Bryan Cooper Guest

    Did you try out this solution?

     
    Bryan Cooper, Oct 23, 2004
    #9
  10. Paul Tomblin

    Bryan Cooper Guest

    If that didn't help, try this link, it may help.

    http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/FocusSpec.html

     
    Bryan Cooper, Oct 23, 2004
    #10
  11. Paul Tomblin

    Paul Tomblin Guest

    Not yet. I was hoping there was an easier solution first. I mean,
    JOptionPane guis do it, native guis do it, and the "setDefaultButton"
    thing does it, so why would it be so complicated in JDialogs?

    The source code for JOptionPane is pretty simple, but it looks like all
    the complicated stuff is in plaf/basic, and it seems to do something
    almost as complicated as yours.
     
    Paul Tomblin, Oct 23, 2004
    #11
  12. Paul Tomblin

    Paul Tomblin Guest

    I did try it. And if you call "setDefaultButton" on 5 buttons, then it
    doesn't matter which one has focus, the last one you called it on gets
    invoked when you hit enter. The only way I could see using this is to
    have a FocusListener that calls setDefaultButton on any button that gets
    focus. Hmmmmm.....
     
    Paul Tomblin, Oct 23, 2004
    #12
  13. (Paul Tomblin) wrote in @allhats.xcski.com:
    You don't have to. Once the default button is set (and since there can
    only be a single default button at any time, yes, the last one you set it
    on will be the default button -- you did read the docs, right?) then any
    button that gets the focus after that becomes the default button until
    you tab out of the buttons again. Did you try tabbing to the buttons
    after setting a default?

    This is the way JOptionPane seems to work, and the way it works in my
    applications.

    You only have to do more work if you want something other than buttons to
    respond to ENTER.

    Chas Douglass
     
    Chas Douglass, Oct 23, 2004
    #13
  14. Paul Tomblin

    Paul Tomblin Guest

    Yes, that's why I said it doesn't matter which one has focus. I set it,
    and I tab between the buttons, and it doesn't matter, the last button
    always gets the enter key.
     
    Paul Tomblin, Oct 23, 2004
    #14
  15. Paul Tomblin

    Bryan Cooper Guest

    I've put together a sample TestDialog and TestPanel class that use the
    DefaultKeyHandler class that I posted. I think this test case matches the
    behavior you're looking for. I don't think there is a one-line (or just a
    couple of lines of code) solution to this one because of how focus works in
    Java.

    Code is attached. Hope this helps you.

    Best Regards,
    Bryan Cooper

    file [TestDialog.java]:
    package testdialog;

    import java.awt.HeadlessException;

    import javax.swing.JDialog;
    import javax.swing.WindowConstants;

    /**
    * @author Bryan
    *
    */
    public class TestDialog extends JDialog {
    /**
    * @throws java.awt.HeadlessException
    */
    public TestDialog()
    throws HeadlessException {
    super();
    initGUI();
    }

    /**
    * Initialize the dialog and show it.
    */
    private void initGUI() {
    setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
    getContentPane().add(new TestPanel());
    setTitle("Test Dialog");
    setModal(true);
    setSize(800, 600);
    setVisible(true);
    }

    public static void main(String[] args) {
    new TestDialog();
    }
    }

    file [TestPanel.java]:
    package testdialog;

    import java.awt.event.ActionEvent;
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;

    import javax.swing.AbstractAction;
    import javax.swing.Action;
    import javax.swing.JButton;
    import javax.swing.JPanel;

    /**
    * The test panel with some buttons.
    */
    public class TestPanel extends JPanel {
    /** Handles key mappings */
    private final DefaultKeyHandler keyHandler = new DefaultKeyHandler();

    private JButton[] buttons = new JButton[5];
    private Action[] actions = new Action[5];

    public TestPanel() {
    for (int i = 0; i < buttons.length; i++) {
    /** Create five buttons and assign them actions */
    final String actionName = "Button " + i;
    actions = new AbstractAction() {
    {
    putValue(Action.NAME, actionName);
    }

    public void actionPerformed(ActionEvent e) {
    System.out.println("I am action: " + actionName);
    }
    };
    buttons = new JButton(actions);
    buttons.addFocusListener(new FocusListener() {
    public void focusGained(FocusEvent e) {
    System.out.println("Focus gained on " + e.getSource());
    /**
    * This will switch your Enter key mapping
    * whenever focus has been gained on a button.
    */
    keyHandler.mapEnterKeyAction(((JButton)e.getSource()).getAction());
    }
    public void focusLost(FocusEvent e) {
    System.out.println("Focus lost on " + e.getSource());
    }
    });
    add(buttons);
    }
    /** Register the panel with the keyHandler */
    keyHandler.registerComponent(this);
    }
    }

    file [DefaultKeyHandler.java]:
    package testdialog;

    import java.awt.Component;
    import java.awt.Container;
    import java.awt.DefaultKeyboardFocusManager;
    import java.awt.KeyEventDispatcher;
    import java.awt.KeyEventPostProcessor;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.util.HashMap;
    import java.util.Set;

    import javax.swing.Action;

    /**
    * This class handles the default key strokes the users type
    * in a panel or component by executing the registered actions.
    * If no actions are registered, then nothing will happen. Any key
    * stroke can be mapped to an action, however, the two main
    * key strokes mapped in this object are the \<enter\> and
    * \<escape\> keys. The key is a KeyEvent.VK_<key> static
    * value and the action is what will be executed if
    * the keystroke is typed by the user.
    * @author B. Cooper
    * @since v1.0.0
    */
    public class DefaultKeyHandler
    extends DefaultKeyboardFocusManager
    implements KeyListener, KeyEventDispatcher, KeyEventPostProcessor {
    /** The table that will hold the actions to execute */
    private HashMap actionMap = new HashMap();

    /**
    * Default Constructor
    */
    public DefaultKeyHandler() {
    super();
    }

    /**
    * @see
    java.awt.KeyEventDispatcher#dispatchKeyEvent(java.awt.event.KeyEvent)
    */
    public boolean dispatchKeyEvent(KeyEvent e) {
    /** Add custom code here if you need to... */
    return false;
    }

    /**
    * If a KeyEvent matches the key in the actionMap object,
    * the associated action will be fired.
    * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
    */
    public void keyPressed(KeyEvent e) {
    int keyCode = e.getKeyCode();
    Set keys = actionMap.keySet();
    Integer findKey = new Integer(keyCode);
    if (keys.contains(findKey)) {

    final Action a = (Action)actionMap.get(findKey);
    try {
    a.actionPerformed(
    new ActionEvent(
    this,
    1,
    (String)a.getValue(Action.ACTION_COMMAND_KEY)));
    } catch (Exception ex) {
    ex.printStackTrace(System.err);
    }
    }
    }

    /**
    * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
    */
    public void keyReleased(KeyEvent e) {
    }

    /**
    * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
    */
    public void keyTyped(KeyEvent e) {
    }

    /**
    * This method will map an action to the enter key event
    * @param action
    */
    public void mapEnterKeyAction(Action action) {
    actionMap.put(new Integer(KeyEvent.VK_ENTER), action);
    }

    /**
    * This method will map an action to the escape key event
    * @param action
    */
    public void mapEscapeKeyAction(Action action) {
    actionMap.put(new Integer(KeyEvent.VK_ESCAPE), action);
    }

    /**
    * This method will map an action to the escape key event
    * @param keyEventId
    * @param action
    */
    public void mapKeyAction(int keyEventId, Action action) {
    actionMap.put(new Integer(keyEventId), action);
    }

    /**
    * @see
    java.awt.KeyEventPostProcessor#postProcessKeyEvent(java.awt.event.KeyEvent)
    */
    public boolean postProcessKeyEvent(KeyEvent e) {
    /** Add custom code here if you need to... */
    return false;
    }

    /**
    * @see
    java.awt.KeyEventPostProcessor#postProcessKeyEvent(java.awt.event.KeyEvent)
    */
    public void processKeyEvent(Component component, KeyEvent e) {
    /** Add custom code here if you need to... */
    }

    /**
    * This method registers a component for the mapped actions.
    * @param component
    */
    public void registerComponent(Component component) {
    component.addKeyListener(this);
    if (component instanceof Container) {
    Component[] comps = ((Container)component).getComponents();
    for (int i = 0; i < comps.length; i++) {
    registerComponent((comps));
    }
    }
    }
    }
     
    Bryan Cooper, Oct 24, 2004
    #15
  16. Paul Tomblin

    Paul Tomblin Guest

    Thanks a lot for this, Bryan. I'll give it a try at work tomorrow.
     
    Paul Tomblin, Oct 24, 2004
    #16
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.