1

Can you please help me how to use KeyBinding and with Consume for typed Chars together, same way as demostraded my SSCCE by using KeyListener

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;

public class Login {

    private static final long serialVersionUID = 1L;
    /* PassWord for unlock*/
    private PswChecker checker = new PswChecker("pass");

    public Login() {
        JTextField firstField = new JTextField(10);
        firstField.addKeyListener(passwordKeyListener);
        JLabel firstLabel = new JLabel("Password is 'pass' ", JLabel.RIGHT);
        firstLabel.setLabelFor(firstField);
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(0, 2, 5, 5));
        p.add(firstLabel);
        p.add(firstField);
        JFrame f = new JFrame("login");
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.setContentPane(p);
        f.setLocationByPlatform(true);
        f.pack();
        f.setVisible(true);
    }
    //
    private KeyListener passwordKeyListener = new KeyListener() {

        private boolean enabled = true;

        @Override
        public void keyTyped(KeyEvent e) {
            if (!enabled) {
                return;
            }
            if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
                boolean b = checker.accept(e.getKeyChar());
                e.consume();
                if (b) {
                    enabled = false;
                    if (e.getComponent() != null) {
                        e.getComponent().removeKeyListener(this);
                    }
                    unlock();
                }
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
        }
    };

    void unlock() {
        JOptionPane.showMessageDialog(null, "unlocked");
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                Login log = new Login();
            }
        });
    }

    class PswChecker {

        private String password = null;
        private boolean unlocked = false;
        private long lastInputTimestamp = 0L;
        private int index = 0;

        public PswChecker(String password) {
            if (password == null) {
                throw new IllegalArgumentException("Null password");
            }
            if (password.trim().length() == 0) {
                throw new IllegalArgumentException("Empty password");
            }
            this.password = password;
        }

        public boolean accept(char c) {
            if (unlocked) {
                return true;
            }
            long timestamp = System.currentTimeMillis();
            if (timestamp - lastInputTimestamp > 700) {
                index = 0;
            }
            lastInputTimestamp = timestamp;
            if (password.charAt(index) == c) {
                index++;
            } else {
                if (password.charAt(0) == c) {
                    index = 1;
                } else {
                    index = 0;
                }
            }
            unlocked = (index == password.length());
            return unlocked;
        }

        public boolean isUnlocked() {
            return unlocked;
        }

        public boolean isLocked() {
            return !unlocked;
        }

        @Override
        public String toString() {
            return unlocked ? "UNLOCKED" : "LOCKED";
        }

        /*private boolean check(String keystrokes, String password, boolean expectUnLocked) {
        PswChecker checker = new PswChecker(password);
        for (int i = 0; i < keystrokes.length(); i++) {
        checker.accept(keystrokes.charAt(i));
        }
        return checker.isUnlocked();
        }*/
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • +1 Is this just an example, or should you use `JPasswordField`? – trashgod Mar 08 '12 at 00:51
  • @trashgod right my question isn't about JPasswordField, about consume events from KeyBindings, – mKorbel Mar 08 '12 at 07:10
  • if it's not a password, what else is it? What exactly do you want to _achieve_? – kleopatra Mar 08 '12 at 11:10
  • right this isn't about password, `1)` I don't want to use (maybe I know how to use) `DocumentListener` or `DocumentFilter`, 2) want listening by `KeyBindings` into `JTextComponent` and determine if (in this case is this logics translated to the password checker) is some `Chars` typed and override `event.consume()` for the output to the `View`. `3)` now I'm able to use only `KeyListener` for `event.consume()`, – mKorbel Mar 08 '12 at 11:21
  • whyyyyyyyyyyyyy? (sorry for near shouting, this 15 letter limit is ... :) – kleopatra Mar 08 '12 at 13:02
  • @kleopatra my goal is catch and consume particular chars or continous chars sequence from SubSystem, then there are only KeyBindings, and Controler can decided about displaying in the GUI, hmmmm now I see that my code example invoking ****, aaaaaaaaaaaaaahhh I hoped that someone without thinking about reasons to move me to the correct direction(s), now I see that my question is maybe idiotic asked ... – mKorbel Mar 08 '12 at 13:09

1 Answers1

3

For security, consider JPasswordField, illustrated here. This would allow use of a DocumentFilter, discussed here.

Addendum: Even for the more general case, I'd use a DocumentFilter, as shown below. I'd use key bindings for sharing an Action among components, as shown in this keypad example.

Addendum: To illustrate @kleopatra's comment, I've updated the code to bind ESC to Reset. As a practical matter, I'd use only keys that aren't already bound to text field actions or required for normal use.

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.text.*;

/** @see https://stackoverflow.com/q/9610386/230513 */
public class Login {

    private static final String PWD = "pass";
    private static final String RESET = "Reset";
    private PlainDocument doc = new PlainDocument();
    private JTextField text = new JTextField(doc, "", 10);

    public Login() {
        doc.setDocumentFilter(new FieldFilter(PWD));
        JLabel label = new JLabel("Password is '" + PWD + "'", JLabel.RIGHT);
        label.setLabelFor(text);
        text.setToolTipText("Press ESC to reset.");
        text.getInputMap().put(
            KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), RESET);
        text.getActionMap().put(RESET, new Reset());
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(0, 2, 5, 5));
        p.add(label);
        p.add(text);
        JFrame f = new JFrame("Login");
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.setContentPane(p);
        f.setLocationByPlatform(true);
        f.pack();
        f.setVisible(true);
    }

    private static class FieldFilter extends DocumentFilter {

        private String password;
        private boolean unlocked;
        private StringBuilder sb = new StringBuilder();

        public FieldFilter(String password) {
            this.password = password;
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length,
            String text, AttributeSet attrs) throws BadLocationException {
            if (unlocked) {
                super.replace(fb, offset, length, text, attrs);
            } else {
                sb.append(text);
                unlocked = password.equals(sb.toString());
            }
        }

        public void reset() {
            sb.delete(0, sb.length());
            unlocked = false;
        }
    }

    private static class Reset extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent e) {
            JTextField jtf = (JTextField) e.getSource();
            PlainDocument doc = (PlainDocument) jtf.getDocument();
            try {
                doc.remove(0, doc.getLength());
            } catch (BadLocationException ex) {
                ex.printStackTrace(System.err);
            }
            FieldFilter filter = (FieldFilter) doc.getDocumentFilter();
            filter.reset();
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Login log = new Login();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • great, nice, thank you (previously) up_voted +1, please apologize me, my goal is understand how to KeyBindings works :-( – mKorbel Mar 08 '12 at 13:25
  • 1
    @mKorbel the underlying keyEvent is consumed if a) a binding is found in any of the inputMaps that are searched and b) the bound action isEnabled – kleopatra Mar 08 '12 at 13:29
  • @kleopatra these two methods are required for basic funcionalites for KeyBindings, are you talking about moving listener to the first parent(JPanel or...) and from Action to distribute only the desired events to the (in this case chars typed from keyboard) JTextField or am I wrong again, – mKorbel Mar 08 '12 at 13:38
  • @mKorbel which two methods do you mean? Anyway, without you elaborating on the why (in your question, please) and especially why the usual approaches don't do what you want, there's is nothing I can do ;-) – kleopatra Mar 08 '12 at 14:18
  • @kleopatra: I've updated the example to illustrate your point; critical review welcome. – trashgod Mar 08 '12 at 19:16
  • I can't be able to create code with KeyBindings (reason why I asked this question) – mKorbel Mar 08 '12 at 20:19
  • @mKorbel: I hope this helped. Please don't hesitate to update the question if needed. – trashgod Mar 08 '12 at 20:31