3

I have created a JButton class that recieving Action, the JButton class includes keystrokes & mouse listener so i can use the same class in multiple frames as needed.

My problems is that: JButton not getting the focus when pressing the key, but it doing the action. i need to make a new background or something that tell the user that the button did the action.

Any ideas??

Here is my code:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.border.LineBorder;
import swtdesigner.SwingResourceManager;

public class IButtonSave extends JButton{
    private static final long serialVersionUID = 1L;
    private Action action = null;
    public IButtonSave() {
        super();
        setFocusPainted(true);
        setFocusable(true);

        try {
            jbInit();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void jbInit() throws Exception {
        setMargin(new Insets(0, 0, 0, 0));
        setBorder(new LineBorder(Color.black, 1, true));
        setIconTextGap(0);
        setHorizontalTextPosition(SwingConstants.CENTER);
        setVerticalTextPosition(SwingConstants.TOP);
        setPreferredSize(new Dimension(50, 43));
        setMinimumSize(new Dimension(50, 43));
        setMaximumSize(new Dimension(50, 43));
        addMouseListener(new ThisMouseListener());
        setVerifyInputWhenFocusTarget(true);
    }

    public void setAction(Action a){
        action = a;
        KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F1,0,true);
        KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0);
        getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "Save");
        getInputMap(JComponent.WHEN_FOCUSED).put(ks2, "Save");

        getActionMap().put("Save", a);
        setText("Save [F1]");
        setIcon(SwingResourceManager.getIcon(SwingResourceManager.class, "/images/small/save.png"));
        setToolTipText("[F1]");
    }

    private class ThisMouseListener extends MouseAdapter {
        public void mouseClicked(MouseEvent e) {
            this_mouseClicked(e);
        }
    }
    protected void this_mouseClicked(MouseEvent e) {
        if(e.getClickCount() >= 1){
            action.actionPerformed(null);
        }
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
Motasem
  • 599
  • 1
  • 6
  • 13
  • Any reason for not calling `super.setAction`? – Guillaume Polet Nov 26 '12 at 13:40
  • no there is no problem in calling it in your way, the button working just fine but when pressing the key F1, the action is done but the focus is never go to the button. what i need to do is to make the button focused and pressed when the action performed. – Motasem Nov 26 '12 at 13:53
  • For better help sooner, post an [SSCCE](http://sscce.org/) (this example needs at least a `main(String[])` to show an `IButtonSave` on-screen in an option pane). – Andrew Thompson Nov 26 '12 at 13:53
  • IButtonSave is a class that i called in each new dialog or frame that i need to make action save to the database. – Motasem Nov 26 '12 at 13:55
  • More details, IF user press F1 then the button become red color for example, but i need to do that in the action side when action is invoked. – Motasem Nov 26 '12 at 13:59

2 Answers2

5

Why extend JButton class when you can simply add KeyBindings to its instance?

Not to sure what you want but this works fine for me:

Basically a JButton which can be activated by mouse click, or pressing F1 (as long as focus is in window and if pressed will shift focus to JButton) or ENTER (only when in focus of JButton).

When the AbstractAction is called it will call requestFocusInWindow() on JButton (thus pressing F1 will make button gain focus which is what I think you wanted):

enter image description here

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class Test {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                final JButton btn = new JButton("Button");

                AbstractAction aa = new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        System.out.println("Here");

                        btn.requestFocusInWindow();//request that the button has focus
                    }
                };

                //so button can be pressed using F1 and ENTER
                btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
                btn.getActionMap().put("Enter", aa);
                btn.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "F1");
                btn.getActionMap().put("F1", aa);

                btn.addActionListener(aa);//so button can be clicked

                JTextField tf = new JTextField("added to show ENTER wont work unless button in focus");

                frame.add(tf);
                frame.add(btn, BorderLayout.SOUTH);

                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

UPADTE:

alternatively as @GuillaumePolet suggested (+1 to him) override processKeyBinding of JButton and check for appropriate key and than call the method:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class Test {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                final JButton btn = new JButton("Button") {
                    @Override
                    protected boolean processKeyBinding(KeyStroke ks, KeyEvent ke, int i, boolean bln) {
                        boolean b = super.processKeyBinding(ks, ke, i, bln);

                        if (b && ks.getKeyCode() == KeyEvent.VK_F1) {
                            requestFocusInWindow();
                        }

                        return b;
                    }
                };

                AbstractAction aa = new AbstractAction() {
                    @Override
                    public void actionPerformed(ActionEvent ae) {
                        System.out.println("Here");
                    }
                };

                //so button can be pressed using F1 and ENTER
                btn.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
                btn.getActionMap().put("Enter", aa);
                btn.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "F1");
                btn.getActionMap().put("F1", aa);

                btn.addActionListener(aa);//so button can be clicked

                JTextField tf = new JTextField("added to show ENTER wont work unless button in focus");

                frame.add(tf);
                frame.add(btn, BorderLayout.SOUTH);

                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}
David Kroukamp
  • 36,155
  • 13
  • 81
  • 138
  • Mnemonic must be combined with the L&F mouseless modifier (usually the Alt key). Also, the Action could be directly set on the JButton instead of manually handling all this or using ActionListener – Guillaume Polet Nov 26 '12 at 13:43
  • that's correct 100% but if you press F1 the button will not focused – Motasem Nov 26 '12 at 14:08
  • @Motasem because it shouldnt if you want this to happen simply call `requestFocusInWindow()` on button instance – David Kroukamp Nov 26 '12 at 14:09
3

Not sure this is the best way to do it, but it works decently enough and if it is not the best way, you get a free SSCCE out of this answer.

I override processKeyBindings() and if it returns true then I grab the focus to indicate that the action has been performed. If you want to do something else you just need to modify the code there.

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

public class IButtonSave extends JButton {
    private static final long serialVersionUID = 1L;

    public IButtonSave() {
        super();
        setFocusPainted(true);
        setFocusable(true);
    }

    @Override
    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
        boolean processKeyBinding = super.processKeyBinding(ks, e, condition, pressed);
        if (processKeyBinding) {
            requestFocusInWindow();
        }
        return processKeyBinding;
    }

    @Override
    public void setAction(Action a) {
        super.setAction(a);
        KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0, true);
        KeyStroke ks2 = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
        getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, "Save");
        getInputMap(JComponent.WHEN_FOCUSED).put(ks2, "Save");
        getActionMap().put("Save", a);
        setText("Save [F1]");
        setToolTipText("[F1]");
    }

    protected void initUI() {
        JFrame frame = new JFrame(IButtonSave.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTextArea textarea = new JTextArea(5, 30);
        JPanel buttonPanel = new JPanel();
        AbstractAction someAction = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                System.err.println("Action performed");
            }
        };
        IButtonSave button = new IButtonSave();
        button.setAction(someAction);
        buttonPanel.add(button);
        frame.add(new JScrollPane(textarea));
        frame.add(buttonPanel, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new IButtonSave().initUI();
            }
        });
    }

}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117