I am writing a Swing program that has a JTable; I want to use control-V to paste into the JTable, and control-S to save the information that's on the JTable.
I first used JTable.registerKeyboardAction()
to register the action with the control-V key, and that works, but I noticed that its javadoc says it is obsolete and that new code should use the input map and action map for this purpose.
I had used those for the control-S key, which I had also mapped to a JButton, so I figured it would be easy to duplicate. Here's the code snippet I have now for creating the JFrame:
private JFrame createMainframe()
{
JFrame frame = new JFrame("VisaExtraction");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String saveFileActionName = "Save";
String pasteActionName = "Paste";
Action saveFileAction = new SaveFileAction(saveFileActionName, frame, tableModel);
Action pasteAction = new PasteAction(pasteActionName, frame, tableModel);
// JButton saveButton = new JButton(saveFileAction);
// saveButton.setMnemonic(KeyEvent.VK_S);
JPanel topPanel = new JPanel();
// topPanel.add(saveButton);
mainTable = new LastColumnChangesWidthJTable(tableModel);
JScrollPane scrollPane = new JScrollPane(mainTable);
// set ctrl-s to the 'saveFile' action
// and ctrl-v to the 'paste' action
InputMap tableInputMap = mainTable.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
KeyStroke saveKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK);
tableInputMap.put(saveKeystroke, saveFileActionName);
KeyStroke pasteKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK);
tableInputMap.put(pasteKeystroke, pasteActionName);
// set the saveFile and paste actions to be executed when invoked.
ActionMap tableActionMap = mainTable.getActionMap();
tableActionMap.put(saveFileActionName, saveFileAction);
tableActionMap.put(pasteActionName, pasteAction);
frame.add(topPanel, BorderLayout.NORTH);
frame.add(scrollPane, BorderLayout.CENTER);
frame.pack();
// register ctrl-v to paste into the JTable
// mainTable.registerKeyboardAction
// ( actionListener -> handlePaste(tableModel, new VisaExtractionListener(tableModel)),
// KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
// JComponent.WHEN_IN_FOCUSED_WINDOW
// );
return frame;
}
As you can see, I'm doing the same things with "S" and "save" that I'm doing with "V" and "paste", but after I start the program, "control-S" works (to the extent of telling me there's nothing to save) and "control-V" does not (the breakpoint in the actionPerformed()
method is not hit).
What could be causing the difference between these two?
Your problem is likely due to the JTable already using ctrl-V action in another input map. Understand that components have 3 input maps, and I believe that the one for JComponent.WHEN_FOCUSED
has precedence over WHEN_IN_FOCUSED_WINDOW
.
My MCVE for proof of concept. Change the commented field in the code below to see which works:
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
Integer[][] rowData = {{1, 2}, {3, 4}};
String[] columnNames = {"A", "B"};
JTable table = new JTable(rowData, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
// int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
int condition = JComponent.WHEN_FOCUSED;
InputMap inputMap = table.getInputMap(condition);
ActionMap actionMap = table.getActionMap();
KeyStroke saveKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK);
KeyStroke pasteKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK);
inputMap.put(saveKeystroke, saveKeystroke.toString());
inputMap.put(pasteKeystroke, pasteKeystroke.toString());
actionMap.put(saveKeystroke.toString(), new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("save action");
}
});
actionMap.put(pasteKeystroke.toString(), new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("paste action");
}
});
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Again in the future, the effort of creating the MCVE should be yours since you are the one asking for help.
Edit: I was wrong. The input map that was already in use and was messing you up was the JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
map. This had a non-null key String for the control-V key in my MCVE update:
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createGui());
}
public static void createGui() {
Integer[][] rowData = { { 1, 2 }, { 3, 4 } };
String[] columnNames = { "A", "B" };
JTable table = new JTable(rowData, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
int condition = JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
// int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
// int condition = JComponent.WHEN_FOCUSED;
InputMap inputMap = table.getInputMap(condition);
ActionMap actionMap = table.getActionMap();
KeyStroke saveKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK);
KeyStroke pasteKeystroke = KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK);
String pasteKey = (String) inputMap.get(pasteKeystroke);
System.out.println(pasteKey);
inputMap.put(saveKeystroke, saveKeystroke.toString());
inputMap.put(pasteKeystroke, pasteKeystroke.toString());
actionMap.put(saveKeystroke.toString(), new MyAction("Save Action"));
actionMap.put(pasteKeystroke.toString(), new MyAction("Paste Action"));
JFrame frame = new JFrame("Foo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(scrollPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
@SuppressWarnings("serial")
class MyAction extends AbstractAction {
private String text;
public MyAction(String text) {
this.text = text;
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println(text);
}
}