Как удалить действие Ctrl+C в JFileChooser?
Я встраиваю JFileChooser
в моей программе в моем собственном кадре с другими пользовательскими компонентами в кадре. Вот дизайн моего приложения, так как оно может помочь визуализировать мои проблемы:
Если вы не можете сказать, списки прямо под JFrame
названия JFileChoosers
, Это должно сработать, когда вы назначаете ярлыки адресатам, а затем, когда вы нажимаете эти сочетания клавиш, выбранный файл перемещается в пункт назначения.
Моя стратегия заключается в том, чтобы назначить ярлык javax.swing.JComponent.WHEN_IN_FOCUSED_WINDOW
сфера действия InputMap
всего кадра.
Но что раздражает, так это то, что JFileChooser
) продолжает реагировать / поглощать нажатия клавиш, которые я не хочу. Например, если я нажму Ctrl+C
мое быстрое действие не запускается. Я пробовал это с родным Look and Feel (я использую windows 7) и L&F по умолчанию, и обе ситуации имеют одну и ту же проблему. Я думаю, что это может быть попытка сделать действие копирования выбранного файла в JFileChooser
потому что если я нажму на одну из кнопок, чтобы заставить ее потерять фокус, внезапно мой Ctrl+C
Команда выполняет мои действия.
Но я не совсем уверен, как JFileChooser
делает это. Когда я звоню getKeyListeners()
на нем он возвращает пустой массив. Я также попытался очистить его карту ввода для этой комбинации клавиш во всех трех областях, и она все еще, кажется, поглощает нажатие клавиши.
Может кто-нибудь дать мне пример кода, который делает JFileChooser
игнорировать Ctrl+C
? Кроме того, было бы полезно, если бы кто-то мог рассказать мне, как отлаживать подобные проблемы в будущем.
Вот код того, что я пробовал до сих пор. Вы также можете использовать это, чтобы попытаться проверить это самостоятельно, так как этот код компилируется и выполняется как есть:
package com.sandbox;
import javax.swing.*;
import java.awt.event.ActionEvent;
public class Sandbox {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control C"), "println");
panel.getActionMap().put("println", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
System.out.println("The JPanel action was performed!");
}
});
panel.add(buildFileChooser()); //if you comment out this line, Ctrl+C does a println, otherwise my action is ignored.
frame.setContentPane(panel);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private static JFileChooser buildFileChooser() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.getActionMap().clear(); //I've tried lots of ideas like this, but the JFileChooser still responds to Ctrl+C
return fileChooser;
}
}
ОБНОВЛЕНИЕ: я дошел до того, что рекурсивно очистил inputMaps и удалил keyListeners из JFileChooser и всех его дочерних компонентов, а JFileChooser все еще проглатывает мою команду Ctrl+C. Вот код, который я использовал для этого (я передал свой JFileChooser в это):
private static void removeKeyboardReactors(JComponent root) {
System.out.println("I'm going to clear the inputMap of: " + root);
root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).clear();
root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).clear();
root.getInputMap(JComponent.WHEN_FOCUSED).clear();
root.getActionMap().clear();
if (root.getRootPane() != null) {
removeKeyboardReactors(root.getRootPane());
}
for (KeyListener keyListener : root.getKeyListeners()) {
root.removeKeyListener(keyListener);
}
for (Component component : root.getComponents()) {
if (component instanceof JComponent) {
removeKeyboardReactors((JComponent) component);
} else if (component instanceof Container) {
Container container = (Container) component;
for (Component containerComponent : container.getComponents()) {
if (containerComponent instanceof JComponent) {
removeKeyboardReactors((JComponent) containerComponent);
} else {
System.out.println("This Container Component was not a JComponent: " + containerComponent);
}
}
} else {
System.out.println("This was not a JComponent: " + component);
}
}
}
2 ответа
в подробном представлении все еще будет заполненная карта ввода
Я подозреваю, что разница между представлением сведений и представлением списка заключается в том, что один использует JTable, а другой - JList. Поэтому я думаю, что вам нужно только удалить привязки из JTable представления сведений.
Это можно сделать без создания панели сведений:
InputMap im = (InputMap)UIManager.get("Table.ancestorInputMap");
KeyStroke ctrlC = KeyStroke.getKeyStroke("control C");
//im.put(ctrlC, "none");
im.remove(ctrlC);
Опять же, следует отметить, что это решение (наряду с решением, которое у вас есть в настоящее время) удалит функциональность Ctrl+C по умолчанию для всех компонентов, а не только для тех, которые были созданы для JFileChooser.
Редактировать:
Разве он не должен удалять его только из тех, из которых я его удаляю?
Ваш код использует метод getParent() для получения InputMap, который содержит привязку. Эта InputMap является общей для всех экземпляров компонента. Компонент будет иметь уникальные привязки только при использовании:
component.getInputMap(...).put(...);
То есть привязка добавляется к компонентам InputMap, а не к его родителям InputMap.
Как вы узнали, что можете сделать это, и это правильно
Смотрите UIManager по умолчанию. Этот список значений по умолчанию для данного LAF. Я не знаю, правильно ли это делать. Насколько я знаю, эффект такой же, как и код, который вы используете сейчас. Это просто еще один способ или удаление привязки из InputMap без необходимости наличия фактического компонента для доступа к родительским InputMap.
Второе редактирование:
Некоторый простой код для отображения InputMaps одинаков:
public static void main(String[] args)
{
JButton first = new JButton("button");
System.out.println(first.getInputMap().getParent());
InputMap im = (InputMap) UIManager.get("Button.focusInputMap");
System.out.println(im);
}
Видимо у InputMaps могут быть родители. Таким образом, ваша очистка всех встроенных ключевых "реакторов" не полностью завершена. Как вы, наверное, догадались, Swing регистрирует определенные привязки клавиатуры по умолчанию для определенных компонентов. В Windows это часто включает Ctrl+C, так как это стандартная горячая клавиша OS для копирования данных в буфер обмена.
Это модифицированный removeKeyboardReactors
получает System.out.println, появляющийся для меня:
private static void removeKeyboardReactors(JComponent root) {
System.out.println("I'm going to clear the inputMap of: " + root);
clearInputMap(root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
clearInputMap(root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
clearInputMap(root.getInputMap(JComponent.WHEN_FOCUSED));
for (KeyListener keyListener : root.getKeyListeners()) {
root.removeKeyListener(keyListener);
}
for (Component component : root.getComponents()) {
if (component instanceof JComponent) {
removeKeyboardReactors((JComponent) component);
} else if (component instanceof Container) {
Container container = (Container) component;
for (Component containerComponent : container.getComponents()) {
if (containerComponent instanceof JComponent) {
removeKeyboardReactors((JComponent) containerComponent);
} else {
System.out.println("This Container Component was not a JComponent: " + containerComponent);
}
}
} else {
System.out.println("This was not a JComponent: " + component);
}
}
}
private static void clearInputMap(InputMap inputMap) {
inputMap.clear();
while ((inputMap = inputMap.getParent()) != null) {
inputMap.clear();
}
}
Я должен был удалить этот код из removeKeyboardReactors
потому что это вызывало переполнение стека:
if (root.getRootPane() != null) {
removeKeyboardReactors(root.getRootPane());
}
Весь модифицированный класс Sandbox следует ниже. Надеюсь, этого будет достаточно, чтобы вы пошли. Если вы хотите, чтобы удаление привязки клавиш было более специфичным для ключа, взгляните на InputMap # remove (KeyStroke).
public class Sandbox
{
public static void main(String[] args)
{
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("control C"), "println");
panel.getActionMap().put("println", new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("The JPanel action was performed!");
}
});
JFileChooser fileChooser = buildFileChooser();
panel.add(fileChooser); //if you comment out this line, Ctrl+C does a println, otherwise my action is ignored.
frame.setContentPane(panel);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
removeKeyboardReactors(fileChooser);
frame.setVisible(true);
}
private static JFileChooser buildFileChooser()
{
JFileChooser fileChooser = new JFileChooser();
fileChooser.getActionMap().clear(); //I've tried lots of ideas like this, but the JFileChooser still responds to Ctrl+C
return fileChooser;
}
private static void clearInputMap(InputMap inputMap)
{
inputMap.clear();
while ((inputMap = inputMap.getParent()) != null)
{
inputMap.clear();
}
}
private static void removeKeyboardReactors(JComponent root) {
System.out.println("I'm going to clear the inputMap of: " + root);
clearInputMap(root.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT));
clearInputMap(root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW));
clearInputMap(root.getInputMap(JComponent.WHEN_FOCUSED));
for (KeyListener keyListener : root.getKeyListeners()) {
root.removeKeyListener(keyListener);
}
for (Component component : root.getComponents()) {
if (component instanceof JComponent) {
removeKeyboardReactors((JComponent) component);
} else if (component instanceof Container) {
Container container = (Container) component;
for (Component containerComponent : container.getComponents()) {
if (containerComponent instanceof JComponent) {
removeKeyboardReactors((JComponent) containerComponent);
} else {
System.out.println("This Container Component was not a JComponent: " + containerComponent);
}
}
} else {
System.out.println("This was not a JComponent: " + component);
}
}
}
}