You can look for a new window opening in the parent window by means of a Swing Worker. Then check if it is a JDialog and extract the button area. Then assign keys to the buttons. This works for the static methods of JOptionPane.
This is the Swing Worker class - just instantiate it and execute it right before the call to show the JOptionPane:
import java.awt.Component;
import java.awt.Window;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.FocusManager;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
public class HotKeyWorker extends SwingWorker<JComponent, Integer>
{
private static final long TIMEOUT = 30000; //Don't wait forever for the JOptionPane
private final String buttonAreaName;
private final Map<String, ButtonData> buttonDataMap;
private final Window owner;
public HotKeyWorker(Component owner, Map<String, ButtonData> buttonDataMap)
{
this.buttonDataMap = buttonDataMap;
if(owner instanceof Window)
this.owner = (Window)owner;
else if(owner != null)
this.owner = SwingUtilities.windowForComponent(owner);
else
this.owner = null;
buttonAreaName = getButtonAreaName();
}
@Override
public JComponent doInBackground()
{
if(owner == null) return null;
if(buttonAreaName == null) return null;
long timeout = System.currentTimeMillis() + TIMEOUT;
Window dialog = null;
while(dialog == null && System.currentTimeMillis() < timeout)
{
dialog = FocusManager.getCurrentManager().getFocusedWindow();
if(dialog != null)
if(dialog.getOwner() != owner)
dialog = null;
}
if(dialog instanceof JDialog)
return getButtonArea(((JDialog)dialog).getRootPane());
return null;
}
@Override
public void done()
{
try
{
JComponent buttonArea = get();
if(buttonArea != null)
for(Component c : buttonArea.getComponents())
if(c instanceof JButton)
setHotKey((JButton)c);
}
catch(InterruptedException | ExecutionException ex) { /* Failed */ }
}
private JComponent getButtonArea(JComponent component)
{
JComponent result = null;
if(component.getName() != null)
if(component.getName().equals(buttonAreaName) && component.getParent() instanceof JOptionPane)
return component;
for(Component c : component.getComponents())
if(c instanceof JComponent)
if((result = getButtonArea((JComponent)c)) != null)
return result;
return result;
}
private void setHotKey(JButton button)
{
if(button.getText().isEmpty()) return;
ButtonData data = buttonDataMap.get(button.getText());
if(data == null) return;
button.setText(data.updatedButtonText);
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(data.hotKeyCode, 0), data.actionKey);
button.getActionMap().put(data.actionKey, new ButtonPress(button));
}
private String getButtonAreaName()
{
JButton trace = new JButton();
Object[] options = { trace };
JOptionPane temp = new JOptionPane();
temp.setOptions(options);
Component buttonArea = trace.getParent();
if(buttonArea != null)
return buttonArea.getName();
return null;
}
}
These are the two helper classes to make things a little cleaner:
public class ButtonData
{
public String updatedButtonText;
public int hotKeyCode;
public String actionKey;
public ButtonData(String updatedButtonText, int hotKeyCode, String actionKey)
{
this.updatedButtonText = updatedButtonText;
this.hotKeyCode = hotKeyCode;
this.actionKey = actionKey;
}
}
and
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
public class ButtonPress extends AbstractAction
{
private final JButton button;
public ButtonPress(JButton button) { this.button = button; }
@Override
public void actionPerformed(ActionEvent event) { button.doClick(); }
}
This is how you would use it:
ButtonData yesData = new ButtonData("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y, "yes");
ButtonData noData = new ButtonData("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N, "no");
ButtonData cancelData = new ButtonData("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C, "cancel");
Map<String, ButtonData> map = new HashMap<>();
map.put("Yes", yesData);
map.put("No", noData);
map.put("Cancel", cancelData);
HotKeyWorker worker = new HotKeyWorker(component, map);
worker.execute();
int result = JOptionPane.showConfirmDialog(component, "Just a text", "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);
Here is a working example:
import javax.swing.JComponent;
import java.awt.Window;
import javax.swing.FocusManager;
import javax.swing.JDialog;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
import javax.swing.AbstractAction;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
public class Test
{
public static void main(String[] args)
{
Frame frame = new Frame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class Frame extends JFrame
{
public Frame()
{
init();
}
private void init()
{
setSize(300,100);
setTitle("Hot Key Test");
add(new Panel());
}
private class Panel extends JPanel
{
private final JButton testButton;
private final JLabel resultLabel;
public Panel()
{
String testKey = "test";
testButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
testButton.addActionListener((event) -> { test(this); });
testButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), testKey);
testButton.getActionMap().put(testKey, new ButtonPress(testButton));
resultLabel = new JLabel("No Result");
init();
}
private void init()
{
add(testButton);
add(resultLabel);
}
private void test(Component component)
{
String anotherTestKey = "Test";
JButton anotherTestButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
anotherTestButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), anotherTestKey);
anotherTestButton.getActionMap().put(anotherTestKey, new ButtonPress(anotherTestButton));
JLabel anotherTestLabel = new JLabel("No Result has been selected");
JPanel testPanel = new JPanel();
testPanel.add(anotherTestButton);
testPanel.add(anotherTestLabel);
anotherTestButton.addActionListener((event) -> { anotherTest(testPanel, anotherTestLabel); });
ButtonData yesData = new ButtonData("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y, "yes");
ButtonData noData = new ButtonData("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N, "no");
ButtonData cancelData = new ButtonData("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C, "cancel");
Map<String, ButtonData> map = new HashMap<>();
map.put("Yes", yesData);
map.put("No", noData);
map.put("Cancel", cancelData);
HotKeyWorker worker = new HotKeyWorker(component, map);
worker.execute();
int result = JOptionPane.showConfirmDialog(component, testPanel, "Confirm", JOptionPane.YES_NO_CANCEL_OPTION);
switch(result)
{
case 0 : resultLabel.setText("Yes Pressed"); break;
case 1 : resultLabel.setText("No Pressed"); break;
case 2 : resultLabel.setText("Cancel Pressed"); break;
default: resultLabel.setText("OptionPane Closed");
}
}
public void anotherTest(Component component, JLabel label)
{
ButtonData fredData = new ButtonData("<html><span style=\"color:Blue;\">F</span>red</html>", KeyEvent.VK_F, "fred");
ButtonData wilmaData = new ButtonData("<html><span style=\"color:Blue;\">W</span>ilma</html>", KeyEvent.VK_W, "wilma");
ButtonData barneyData = new ButtonData("<html>B<span style=\"color:Blue;\">a</span>rney</html>", KeyEvent.VK_A, "barney");
ButtonData bettyData = new ButtonData("<html>B<span style=\"color:Blue;\">e</span>tty</html>", KeyEvent.VK_E, "betty");
Map<String, ButtonData> map = new HashMap<>();
map.put("Fred", fredData);
map.put("Wilma", wilmaData);
map.put("Barney", barneyData);
map.put("Betty", bettyData);
HotKeyWorker worker = new HotKeyWorker(component, map);
worker.execute();
String[] options = {"Fred", "Wilma", "Barney", "Betty" };
int result = JOptionPane.showOptionDialog(component, "Who do you like?", "Confirm", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
switch(result)
{
case 0 : label.setText("I like Fred"); break;
case 1 : label.setText("I like Wilma"); break;
case 2 : label.setText("I like Barney"); break;
case 3 : label.setText("I like Betty"); break;
default: label.setText("I decline to answer");
}
}
private class HotKeyWorker extends SwingWorker<JComponent, Integer>
{
private static final long TIMEOUT = 30000; //Don't wait forever for the JOptionPane
private final String buttonAreaName;
private final Map<String, ButtonData> buttonDataMap;
private final Window owner;
public HotKeyWorker(Component owner, Map<String, ButtonData> buttonDataMap)
{
this.buttonDataMap = buttonDataMap;
if(owner instanceof Window)
this.owner = (Window)owner;
else if(owner != null)
this.owner = SwingUtilities.windowForComponent(owner);
else
this.owner = null;
buttonAreaName = getButtonAreaName();
}
@Override
public JComponent doInBackground()
{
if(owner == null) return null;
if(buttonAreaName == null) return null;
long timeout = System.currentTimeMillis() + TIMEOUT;
Window dialog = null;
while(dialog == null && System.currentTimeMillis() < timeout)
{
dialog = FocusManager.getCurrentManager().getFocusedWindow();
if(dialog != null)
if(dialog.getOwner() != owner)
dialog = null;
}
if(dialog instanceof JDialog)
return getButtonArea(((JDialog)dialog).getRootPane());
return null;
}
@Override
public void done()
{
try
{
JComponent buttonArea = get();
if(buttonArea != null)
for(Component c : buttonArea.getComponents())
if(c instanceof JButton)
setHotKey((JButton)c);
}
catch(InterruptedException | ExecutionException ex) { /* Failed */ }
}
private JComponent getButtonArea(JComponent component)
{
JComponent result = null;
if(component.getName() != null)
if(component.getName().equals(buttonAreaName) && component.getParent() instanceof JOptionPane)
return component;
for(Component c : component.getComponents())
if(c instanceof JComponent)
if((result = getButtonArea((JComponent)c)) != null)
return result;
return result;
}
private void setHotKey(JButton button)
{
if(button.getText().isEmpty()) return;
ButtonData data = buttonDataMap.get(button.getText());
if(data == null) return;
button.setText(data.updatedButtonText);
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(data.hotKeyCode, 0), data.actionKey);
button.getActionMap().put(data.actionKey, new ButtonPress(button));
}
private String getButtonAreaName()
{
JButton trace = new JButton();
Object[] options = { trace };
JOptionPane temp = new JOptionPane();
temp.setOptions(options);
Component buttonArea = trace.getParent();
if(buttonArea != null)
return buttonArea.getName();
return null;
}
}
private class ButtonData
{
public String updatedButtonText;
public int hotKeyCode;
public String actionKey;
public ButtonData(String updatedButtonText, int hotKeyCode, String actionKey)
{
this.updatedButtonText = updatedButtonText;
this.hotKeyCode = hotKeyCode;
this.actionKey = actionKey;
}
}
private class ButtonPress extends AbstractAction
{
private final JButton button;
public ButtonPress(JButton button) { this.button = button; }
@Override
public void actionPerformed(ActionEvent event) { button.doClick(); }
}
}
}
}
This will work for all JOptionPane static methods. This will work fine as long as your not randomly popping up windows in the parent component's window. Note: the parent component of the JOptionPane cannot be null.
Of course, it is probably easier just to instantiate a JOptionPane and customize it. Here are the classes to do that:
import java.awt.Component;
import javax.swing.Icon;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.AbstractAction;
import javax.swing.Action;
import java.awt.event.ActionEvent;
public class HotKeyOptionPane
{
public static int showOptionDialog(Component parentComponent,
Object message, String title,
int optionType, int messageType,
Icon icon, HotKey[] options,
Object initialValue)
{
JButton[] buttons = new JButton[options.length];
for(int i = 0; i < options.length; i++)
buttons[i] = new JButton(options[i].text);
JOptionPane pane = new JOptionPane(message, messageType, optionType, icon, buttons, initialValue);
for(int option = 0; option < buttons.length; option++)
setButtonAction(buttons[option], options[option].keyCode, option, pane);
JDialog dialog = pane.createDialog(parentComponent, title);
dialog.setVisible(true);
if (pane.getValue() instanceof Integer)
return (Integer)pane.getValue();
return -1;
}
private static void setButtonAction(JButton button, int hotKey, Integer option, JOptionPane pane)
{
Action action = new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent event)
{
pane.setValue(option);
pane.firePropertyChange(JOptionPane.VALUE_PROPERTY, JOptionPane.DEFAULT_OPTION, option);
}
};
button.addActionListener(action);
button.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(hotKey, 0), button.getText());
button.getActionMap().put(button.getText(), action);
}
}
and
public class HotKey
{
public String text;
public int keyCode;
public HotKey(String text, int keyCode)
{
this.text = text;
this.keyCode = keyCode;
}
}
And this is how you would use them:
import javax.swing.JComponent;
import java.awt.Component;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JOptionPane;
import javax.swing.JLabel;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
public class Test
{
public static void main(String[] args)
{
Frame frame = new Frame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static class Frame extends JFrame
{
public Frame()
{
init();
}
private void init()
{
setSize(300,100);
setTitle("Hot Key Test");
add(new Panel());
}
private class Panel extends JPanel
{
private final JButton testButton;
private final JLabel resultLabel;
public Panel()
{
String testKey = "test";
testButton = new JButton("<html><span style=\"color:Blue;\">T</span>est</html>");
testButton.addActionListener((event) -> { test(this); });
testButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_T, 0), testKey);
testButton.getActionMap().put(testKey, new ButtonPress(testButton));
resultLabel = new JLabel("No Result");
init();
}
private void init()
{
add(testButton);
add(resultLabel);
}
private void test(Component component)
{
HotKey yesOption = new HotKey("<html><span style=\"color:Blue;\">Y</span>es</html>", KeyEvent.VK_Y);
HotKey noOption = new HotKey("<html><span style=\"color:Blue;\">N</span>o</html>", KeyEvent.VK_N);
HotKey cancelOption = new HotKey("<html><span style=\"color:Blue;\">C</span>ancel</html>", KeyEvent.VK_C);
HotKey[] options = { yesOption, noOption, cancelOption };
int result = HotKeyOptionPane.showOptionDialog(component, "Just a test", "Testing", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, 0);
switch(result)
{
case 0 : resultLabel.setText("Yes Pressed"); break;
case 1 : resultLabel.setText("No Pressed"); break;
case 2 : resultLabel.setText("Cancel Pressed"); break;
default: resultLabel.setText("OptionPane Closed");
}
}
}
}
}
"As far as the laws of mathematics refer to reality, they are not certain, and as far as they are certain, they do not refer to reality."
Albert Einstein