Как правильно удалить автоматически добавляемую закрывающую скобку при ее наборе

в методе textPaneKeyTyped(java.awt.event.keyEvent evt) я написал код для автоматического закрытия квадратных скобок при вводе открывающей. Я добавил некоторый код для удаления закрывающей скобки, когда пользователь вводит ее в textPane, но он ничего не делает.

Это рабочий пример:

package test;

import java.awt.event.ActionEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.text.BadLocationException;


public class NewJFrame extends javax.swing.JFrame {

    /** Creates new form NewJFrame */
    public NewJFrame() {
        initComponents();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        textPanel = new javax.swing.JTextPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        textPanel.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyTyped(java.awt.event.KeyEvent evt) {
                textPanelKeyTyped(evt);
            }
        });
        jScrollPane1.setViewportView(textPanel);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 380, Short.MAX_VALUE)
                .addContainerGap())
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addGap(19, 19, 19)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 254, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(27, Short.MAX_VALUE))
        );

        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);

        pack();
    }// </editor-fold>

private void textPanelKeyTyped(java.awt.event.KeyEvent evt) {
// TODO add your handling code here:
     Action action1 = new AbstractAction() {

        @Override
        public void actionPerformed(ActionEvent e) {
            textModel = (JTextPane) e.getSource();
            try {
                int position2 = textModel.getCaretPosition();
                textModel.getDocument().remove(position2, 1);
                textModel.getDocument().insertString(position2, ")", null);

            } catch (Exception e1) {}
        }

};
    Action action = new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent e) {
                int position = textModel.getCaretPosition();
                textModel.replaceSelection("()");
                textModel.setCaretPosition(position+1);
            }

    };

    String key =  "typed (";
    String key1 = "typed )";
    textPanel.getInputMap().put(KeyStroke.getKeyStroke(key), key);
    textPanel.getInputMap().put(KeyStroke.getKeyStroke(key1), key1);
    textPanel.getActionMap().put(key, action);
    textPanel.getActionMap().put(key1, action1);


}

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                new NewJFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextPane textPanel;
    // End of variables declaration
}

Код для автоматического закрытия скобок работает. Проблема заключается в замене закрывающей скобки: она печатает ')' только тогда, когда перед ней есть '('. Если бы я мог напечатать ')' в любой время, как я должен редактировать свой код? Спасибо.


person Pino    schedule 21.03.2016    source источник
comment
Не могли бы вы отобразить значение pos при удалении?   -  person Arnaud    schedule 21.03.2016
comment
в первой строке textPane, да. Во второй строке появляется только если я закрываю скобку 2 раза, вот так: '(' ')' ')'. при вводе второй закрывающей скобки она появляется. В третьей строке, когда я печатаю 3 раза...   -  person Pino    schedule 21.03.2016
comment
Также не могли бы вы опубликовать простой компилируемый и работающий пример? Я не думаю, что private void textPaneprivate void textModelKeyTyped(java.awt.event.KeyEvent evt) является допустимой сигнатурой метода.   -  person Arnaud    schedule 21.03.2016
comment
Посмотрите на пост, я его отредактировал!   -  person Pino    schedule 21.03.2016


Ответы (1)


Никогда не рекомендуется блокировать использование KeyStroke. Вы никогда не знаете, когда ")" может потребоваться ввести вручную. Кого волнует, вводит ли пользователь ")". Если это синтаксически неправильно, они в конечном итоге получат ошибку компиляции. Умный пользователь поймет, что все, что тогда нужно сделать, это ввести "(" и для него будет введено ")".

Однако, если есть ситуация, когда вы хотите предотвратить ввод данного символа, вы должны использовать Key Bindings НЕ KeyListener.

Например:

KeyStroke ignore = KeyStroke.getKeyStroke(')');
textPane.getInputMap().put(ignore, "none");

Для получения дополнительной информации о Key Bindings прочитайте учебник Swig. Я дал вам ссылку в одном из ваших предыдущих вопросов. Держите ссылку на учебник под рукой, чтобы ознакомиться с основами. Вы найдете раздел How to Use Key Bindings, когда посмотрите на оглавление («тропу») учебника.

Также для вашего действия "(" вы можете просто использовать:

textPane.replaceSelection("()");
person camickr    schedule 21.03.2016
comment
Во-первых, спасибо за полезные советы! Я отредактировал свой вопрос с новым кодом, но, похоже, все еще есть ошибка: ')' печатается только в том случае, если раньше был '('. - person Pino; 21.03.2016
comment
the ')' is print only if there's a '(' before - это было вашим требованием. Вы хотели автоматически вставить () и запретить пользователю вводить ). Я предположил, что это не очень хорошее требование, и вам не следует даже беспокоиться о том, что пользователь печатает ). Все, что вам нужно, это настраиваемое действие для клавиши ( вставить (). В любом случае у вас все еще есть KeyListener. Я понятия не имею, почему. Я специально указал, что вы не должны использовать KeyListener. - person camickr; 21.03.2016
comment
Кроме того, вы не послушались моего предложения использовать метод replaceSelection(). Ваш код меняет способ ввода текста. Обычно, если выделен какой-либо текст и вы вводите символ, то введенный символ заменяет выделенный текст. Однако вы знаете, что у вас есть специальный код, который не заменяет выделенный текст всякий раз, когда вы вводите (. Это несоответствие. - person camickr; 21.03.2016
comment
KeyListener по умолчанию добавляется Netbeans в методе initComponent(). Почему я не должен его использовать? Более того, я мог бы напечатать ')', даже если раньше не было '(', но, тем не менее, я также хочу вставить '()'. Не могли бы вы проверить, что не так в action1? и почему сбой проекта, когда я пытаюсь заменить какой-либо текст на '('? Я знаю, что это много вопросов, но я не могу выбраться из этого. Я также реализовал метод replaceSelection. - person Pino; 21.03.2016
comment
KeyListener is added by default by - Сомневаюсь. Нет причин добавлять KeyListener. Конечно, вы не стали бы создавать Action1 внутри KeyListener. Вы делаете что-то в среде IDE, чтобы она генерировала этот код. Код, который вы разместили, даже не компилируется. Moreover, I'd be able to print a ')' even if there's no '(' before, but, still, I want the insertion of '()' as well - Ничего делать не надо!!! ) является допустимым символом, как и любой другой символ на клавиатуре. Все, что вам нужно, это одно действие, которое обрабатывает ключ (. Избавьтесь от второго действия! - person camickr; 21.03.2016