Как удалить действие Ctrl+C в JFileChooser?

Я встраиваю 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);
            }
        }
    }
}
Другие вопросы по тегам