Andrija
Andrija

Reputation: 1017

How can I add “Default Button” functionality to two buttons in Swing?

In every window/panel I have the Enter key mapped as the defaultButton (meaning that pressing Enter triggers a button press even though the pressed button isn't in focus). What I need to do is map the Escape key to another button which also triggers the second button, regardless of focus. The code for the enter key is:

// We get the button
IButton iButton = (IButton)actionToolbar.getComponent(actionToolbar.getComponentCount() - 1);
// We get the window
Window window = SwingUtilities.getWindowAncestor(this);
// Only these two types of windows should have this behaviour
if (window instanceof JFrame) {
    ((JFrame)window).getRootPane().setDefaultButton(iButton);
}
else if (window instanceof JDialog) {
    ((JDialog)window).getRootPane().setDefaultButton(iButton);
}

Now what I need is basically the same code but with changing Enter with Escape, or adding a listener to.... something, I'm unsure what.

EDIT: I'm have to do this in Java 1.4 which I know it would be great if I said it immediately.

Upvotes: 2

Views: 2272

Answers (4)

Andrija
Andrija

Reputation: 1017

I managed to solve this by adding a key listener to the opened panel, like this

InputMap iMap = theTaskWindow.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
     iMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "escape");
     ActionMap aMap = theTaskWindow.getRootPane().getActionMap();
     aMap.put("escape", new AbstractAction() {
      private static final long serialVersionUID = 1L;
      public void actionPerformed(ActionEvent e){
        doCancel();
      }
     });

Upvotes: 1

mKorbel
mKorbel

Reputation: 109813

or another and very simple way

import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class ExampleKeyStrokes {

    private JFrame frame = new JFrame();
    private JButton button;

    public ExampleKeyStrokes() {
        button = new JButton(new AbstractAction(" push ENTER ") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });
        button.setText(" Please push ENTER ");
        //button.setPreferredSize(new Dimension(200, 50));
        frame.add(button);
        frame.getRootPane().setDefaultButton(button);
        frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("F2"), "clickButton");
        frame.getRootPane().getActionMap().put("clickButton", new AbstractAction() {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                button.doClick();
            }
        });
        frame.setLocation(150, 150);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        setFocusButton();
    }

    private void setFocusButton() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                button.grabFocus();
                button.requestFocus();
                button.requestFocusInWindow();
            }
        });
    }

    static public void main(String[] s) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ExampleKeyStrokes eKS = new ExampleKeyStrokes();
            }
        });
    }
}

Upvotes: 1

Steve McLeod
Steve McLeod

Reputation: 52448

This does it:

window.getContentPane().registerKeyboardAction(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            onCancel();
        }
    }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

Your second JButton should also call onCancel() when clicked. So either pressing escape or clicking on this both will do the same thing.

Upvotes: -1

kleopatra
kleopatra

Reputation: 51525

JXRootPane (of the SwingX project) has it by default, you can do something like

private void installKeyboardActions() {
    Action escAction = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent evt) {
            JButton cancelButton = getCancelButton();
            if (cancelButton != null) {
                cancelButton.doClick(20);
            }
        }

        /**
         * Overridden to hack around #566-swing: 
         * JXRootPane eats escape keystrokes from datepicker popup.
         * Disable action if there is no cancel button.<p>
         * 
         * That's basically what RootPaneUI does - only not in 
         * the parameterless isEnabled, but in the one that passes
         * in the sender (available in UIAction only). We can't test 
         * nor compare against core behaviour, UIAction has
         * sun package scope. <p>
         * 
         * Cont'd (Issue #1358-swingx: popup menus not closed)
         * The extended hack is inspired by Rob Camick's
         * <a href="http://tips4java.wordpress.com/2010/10/17/escape-key-and-dialog/"> Blog </a>
         * and consists in checking if the the rootpane has a popup's actionMap "inserted". 
         * NOTE: this does not work if the popup or any of its children is focusOwner.
         */
        @Override
        public boolean isEnabled() {
            Component component = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
            if (component instanceof JComponent) {
                Action cancelPopup = ((JComponent)component).getActionMap().get("cancel");
                if (cancelPopup != null) return false;
            }
            return (cancelButton != null) && (cancelButton.isEnabled());
        }
    };
    getActionMap().put("esc-action", escAction);
    InputMap im = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
    im.put(key, "esc-action");
}


/**
 * Sets the <code>cancelButton</code> property,
 * which determines the current default cancel button for this <code>JRootPane</code>.
 * The cancel button is the button which will be activated 
 * when a UI-defined activation event (typically the <b>ESC</b> key) 
 * occurs in the root pane regardless of whether or not the button 
 * has keyboard focus (unless there is another component within 
 * the root pane which consumes the activation event,
 * such as a <code>JTextPane</code>).
 * For default activation to work, the button must be an enabled
 * descendant of the root pane when activation occurs.
 * To remove a cancel button from this root pane, set this
 * property to <code>null</code>.
 *
 * @param cancelButton the <code>JButton</code> which is to be the cancel button
 * @see #getCancelButton() 
 *
 * @beaninfo
 *  description: The button activated by default for cancel actions in this root pane
 */
public void setCancelButton(JButton cancelButton) { 
    JButton old = this.cancelButton;

    if (old != cancelButton) {
        this.cancelButton = cancelButton;

        if (old != null) {
            old.repaint();
        }
        if (cancelButton != null) {
            cancelButton.repaint();
        } 
    }

    firePropertyChange("cancelButton", old, cancelButton);        
}

/**
 * Returns the value of the <code>cancelButton</code> property. 
 * @return the <code>JButton</code> which is currently the default cancel button
 * @see #setCancelButton
 */
public JButton getCancelButton() { 
    return cancelButton;
}

Upvotes: 4

Related Questions