package cologne.eck.all_peas.gui;
/*
* Peafactory - Production of Password Encryption Archives
* Copyright (C) 2015 Axel von dem Bruch
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* See: http://www.gnu.org/licenses/gpl-2.0.html
* You should have received a copy of the GNU General Public License
* along with this library.
*/
/**
* A simple keyboard (US layout) to hamper key logging.
*/
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.accessibility.AccessibleContext;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.SwingConstants;
import cologne.eck.all_peas.data.PeaProperties;
import cologne.eck.tools.Zeroizer;
@SuppressWarnings("serial")
public class Keyboard extends JDialog implements ActionListener {
private static final char[][] EN_TABLE = {
{ '~', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+' },// backspace <-
{'\'', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=' },
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\u007B' , '\u007D' , '|'},
{'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\\'},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ':'},
{'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';'},
{' ', ' ', ' ', ' ', ' ', ' ', ' ', '<', '>', '?'},
{'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/'},//shift left, '<', shift right
// extra table for < and > if images fail
{'\u1438', '\u1433'},// U+1433, U+1438 CANADIAN SYLLABICS PA and PO to avoid html conflicts
};
private boolean shiftEnabled = false;
private JPasswordField pswField = null;
private static JPanel topPanel = null;
private static Color bgColor = new Color(234, 234, 234);
private static Color shiftColor = new Color(200,200,200);// show shifting
public Keyboard(Window owner, JPasswordField _pswField) {
super(owner);// sets the image icon of owner and location
this.setTitle( "Keyboard" );
// this is maybe used together with char table
this.setModalityType(Dialog.ModalityType.MODELESS);
this.pswField = _pswField;
this.setAlwaysOnTop(true);
topPanel = (JPanel) this.getContentPane();
topPanel.setBackground(bgColor);
topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
JPanel firstPanel = new JPanel();
firstPanel.setBackground(bgColor);
firstPanel.setLayout(new BoxLayout(firstPanel, BoxLayout.X_AXIS));
topPanel.add(firstPanel);
for (int i = 0; i < EN_TABLE[0].length; i++) {
KeyButton firstLineButton = new KeyButton();
firstLineButton.setText("<html>" + EN_TABLE[0][i] + "<br/>" + EN_TABLE[1][i] + "<html>");
firstLineButton.addActionListener(this);
firstLineButton.setActionCommand("" + EN_TABLE[1][i] + i + 1);
if (i == 0 || (i > 10)){
firstLineButton.setPreferredSize(new Dimension(30, 40));
firstLineButton.setMinimumSize(new Dimension(30, 40));
firstLineButton.setMaximumSize(new Dimension(30, 40));
} else {
firstLineButton.setPreferredSize(new Dimension(40, 40));
firstLineButton.setMinimumSize(new Dimension(40, 40));
firstLineButton.setMaximumSize(new Dimension(40, 40));
}
firstPanel.add(firstLineButton);
}
firstPanel.add(Box.createHorizontalStrut(5));
// use icon for button:
JButton backspace = new KeyButton();
ImageIcon image = IconManager.loadIcon("back24.png", PeaProperties.getBundle().getString("back_arrow"),
PeaProperties.getBundle().getString("back_arrow"),
PeaProperties.getBundle().getString("delete_last_character") );
if (image != null) {
backspace.setIcon(image);
} else { // set characters
backspace.setText(" <-");
backspace.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 15));
}
backspace.addActionListener(this);
backspace.setActionCommand("backspace");
backspace.setPreferredSize(new Dimension(45, 40));
backspace.setMaximumSize(new Dimension(45, 40));
backspace.setMaximumSize(new Dimension(45, 40));
firstPanel.add(backspace);
firstPanel.add(Box.createHorizontalGlue());
JPanel secondPanel = new JPanel();
secondPanel.setBackground(bgColor);
secondPanel.setLayout(new BoxLayout(secondPanel, BoxLayout.X_AXIS));
topPanel.add(secondPanel);
secondPanel.add(Box.createHorizontalStrut(40));
for (int i = 0; i < EN_TABLE[2].length; i++) {
JButton secondLineButton = new KeyButton();
secondLineButton.setText("<html>" + EN_TABLE[2][i] + "<br/>" + EN_TABLE[3][i] + "<html>");
secondLineButton.addActionListener(this);
secondLineButton.setActionCommand("" + EN_TABLE[3][i] + i + 3);
if (i > 9){
secondLineButton.setPreferredSize(new Dimension(30, 40));
secondLineButton.setMinimumSize(new Dimension(30, 40));
secondLineButton.setMaximumSize(new Dimension(30, 40));
} else {
secondLineButton.setPreferredSize(new Dimension(40, 40));
secondLineButton.setMinimumSize(new Dimension(40, 40));
secondLineButton.setMaximumSize(new Dimension(40, 40));
}
secondPanel.add(secondLineButton);
}
secondPanel.add(Box.createHorizontalGlue() );
JPanel thirdPanel = new JPanel();
thirdPanel.setBackground(bgColor);
thirdPanel.setLayout(new BoxLayout(thirdPanel, BoxLayout.X_AXIS));
topPanel.add(thirdPanel);
thirdPanel.add(Box.createHorizontalStrut(50));
for (int i = 0; i < EN_TABLE[4].length; i++) {
JButton thirdLineButton = new KeyButton();
thirdLineButton.setText("<html>" + EN_TABLE[4][i] + "<br/>" + EN_TABLE[5][i] + "<html>");
thirdLineButton.addActionListener(this);
thirdLineButton.setActionCommand("" + EN_TABLE[5][i] + i + 5);
if (i > 8) {
thirdLineButton.setPreferredSize(new Dimension(30, 40));
thirdLineButton.setMinimumSize(new Dimension(30, 40));
thirdLineButton.setMaximumSize(new Dimension(30, 40));
} else {
thirdLineButton.setPreferredSize(new Dimension(40, 40));
thirdLineButton.setMinimumSize(new Dimension(40, 40));
thirdLineButton.setMaximumSize(new Dimension(40, 40));
}
thirdPanel.add(thirdLineButton);
}
thirdPanel.add(Box.createHorizontalStrut(5));
thirdPanel.add(Box.createHorizontalGlue() );
JPanel fourthPanel = new JPanel();
fourthPanel.setBackground(bgColor);
fourthPanel.setLayout(new BoxLayout(fourthPanel, BoxLayout.X_AXIS));
topPanel.add(fourthPanel);
// less than sign and greater than sign result in conflicts with
// html tags and the similar looking
// CANADIAN SYLLABICS PA and PO are not always displayed (Windows 7)
// => icon are used
ImageIcon lessThan = IconManager.loadIcon("kleiner.png",
PeaProperties.getBundle().getString("smaller_sign"),
PeaProperties.getBundle().getString("smaller_sign"),
PeaProperties.getBundle().getString("type_character_less_than") );
ImageIcon greaterThan = IconManager.loadIcon("groesser.png",
PeaProperties.getBundle().getString("bigger_sign"),
PeaProperties.getBundle().getString("bigger_sign"),
PeaProperties.getBundle().getString("type_character_bigger_than") );
JButton shift = new KeyButton("shift");
shift.addActionListener(this);
shift.setActionCommand("shift");
shift.setMargin(new Insets( 10, 1, 5, 1));
shift.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 15));
shift.setPreferredSize(new Dimension(60, 40));
shift.setMinimumSize(new Dimension(60, 40));
shift.setMaximumSize(new Dimension(60, 40));
fourthPanel.add(shift);
fourthPanel.add(Box.createHorizontalStrut(5));
for (int i = 0; i < EN_TABLE[6].length; i++) {
JButton fourthLineButton = new KeyButton();
if (i == 7) {// the button < - this can not be used inside html
// try to use the icon, because Windows does not display the character
if (lessThan != null) {
fourthLineButton.setIcon(lessThan);
fourthLineButton.setVerticalTextPosition(SwingConstants.BOTTOM);
fourthLineButton.setHorizontalTextPosition(SwingConstants.CENTER);
fourthLineButton.setText("" + EN_TABLE[7][i]);
} else { // use the characters CANADIAN SYLLABICS PA and PO
fourthLineButton.setText("<html>" + EN_TABLE[8][0] + "<br/>" + EN_TABLE[7][i] + "<html>");
}
} else if (i == 8) {// the button > - this can not be used inside html
// try to use the icon, because Windows does not display the character
if (greaterThan != null) {
fourthLineButton.setIcon(greaterThan);
fourthLineButton.setVerticalTextPosition(SwingConstants.BOTTOM);
fourthLineButton.setHorizontalTextPosition(SwingConstants.CENTER);
fourthLineButton.setText("" + EN_TABLE[7][i]);
} else { // use the characters CANADIAN SYLLABICS PA and PO
fourthLineButton.setText("<html>" + EN_TABLE[8][1] + "<br/>" + EN_TABLE[7][i] + "<html>");
}
// all other characters: use table
} else {
fourthLineButton.setText("<html>" + EN_TABLE[6][i] + "<br/>" + EN_TABLE[7][i] + "<html>");
}
fourthLineButton.addActionListener(this);
fourthLineButton.setActionCommand("" + EN_TABLE[7][i] + i + 7);
if (i > 6) { // less space for the characters ,./
fourthLineButton.setPreferredSize(new Dimension(30, 40));
fourthLineButton.setMinimumSize(new Dimension(30, 40));
fourthLineButton.setMaximumSize(new Dimension(30, 40));
} else { // more space for letters
fourthLineButton.setPreferredSize(new Dimension(40, 40));
fourthLineButton.setMinimumSize(new Dimension(40, 40));
fourthLineButton.setMaximumSize(new Dimension(40, 40));
}
fourthPanel.add(fourthLineButton);
}
fourthPanel.add(Box.createHorizontalStrut(5));
JButton shift2 = new KeyButton("shift");
shift2.addActionListener(this);
shift2.setActionCommand("shift");
shift2.setMargin(new Insets( 10, 1, 5, 1));
shift2.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 15));
shift2.setPreferredSize(new Dimension(100, 40));
shift2.setMinimumSize(new Dimension(100, 40));
shift2.setMaximumSize(new Dimension(100, 40));
fourthPanel.add(shift2);
fourthPanel.add(Box.createHorizontalGlue() );
JPanel fifthPanel = new JPanel();
fifthPanel.setBackground(bgColor);
fifthPanel.setLayout(new BoxLayout(fifthPanel, BoxLayout.X_AXIS));
topPanel.add(fifthPanel);
JButton space = new KeyButton("space");
space.addActionListener(this);
space.setActionCommand("space");
space.setPreferredSize(new Dimension (390, 30));
space.setMinimumSize(new Dimension (390, 30));
space.setMaximumSize(new Dimension (390, 30));
space.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 16));
fifthPanel.add(Box.createHorizontalStrut(55));
fifthPanel.add(space);
fifthPanel.add(Box.createHorizontalGlue());
// set location: if possible above the password field
int keyboardHeight = this.getPreferredSize().height;
int placeAbovePswField = (int) pswField.getLocationOnScreen().getY()
- pswField.getSize().height;
int positionBelowPswField = (int) pswField.getLocationOnScreen().getY() + 25;
int screenHeight = (int) PswDialogView.getScreenheight();
if (placeAbovePswField > keyboardHeight){
this.setLocation((int) pswField.getLocationOnScreen().getX(),
placeAbovePswField - keyboardHeight);
} else if (keyboardHeight < (screenHeight - positionBelowPswField)){ // set below password field
this.setLocation((int) pswField.getLocationOnScreen().getX(),
positionBelowPswField);
} else { // set position of window owner
this.setLocation(owner.getLocationOnScreen());
}
this.pack();
}
private void setChar(char c) {
if (pswField != null) {
char[] oldPsw = pswField.getPassword();
char[] tmp = new char[oldPsw.length + 1];
System.arraycopy(oldPsw, 0, tmp, 0, oldPsw.length);
tmp[oldPsw.length] = c;
Zeroizer.zero(oldPsw);
//System.out.println("+ char: " + new String(tmp));
pswField.setText(new String(tmp));
Zeroizer.zero(tmp);
} else {
System.err.println("Password field for table was closed.");
}
}
private void deleteLastChar() {
if (pswField != null) {
if (pswField.getPassword().length == 0) {
return;
}
char[] oldPsw = pswField.getPassword();
char[] tmp = new char[oldPsw.length - 1];
System.arraycopy(oldPsw, 0, tmp, 0, tmp.length);
Zeroizer.zero(oldPsw);
//System.out.println("deleted char: " + new String(tmp));
pswField.setText(new String(tmp));
Zeroizer.zero(tmp);
} else {
System.err.println("Password field for table was closed.");
}
}
@Override
public void actionPerformed(ActionEvent ape) {
String command = ape.getActionCommand();
//System.out.println("command: " + command);
if (command.equals("shift")){
if (shiftEnabled == false) {
// change background color to indicate shifting
shiftEnabled = true;
topPanel.getComponent(3).setBackground(shiftColor);
topPanel.getComponent(4).setBackground(shiftColor);
} else {
shiftEnabled = false;
topPanel.getComponent(3).setBackground(bgColor);
topPanel.getComponent(4).setBackground(bgColor);
}
} else if (command.equals("space")){
setChar(' ');
} else if (command.equals("backspace")) {
deleteLastChar();
/* } else if (command.startsWith("" + '\u1438') ) {// '\u1438' U+1438 CANADIAN SYLLABICS PA to avoid html conflicts
setChar('<');
} else if (command.startsWith("" + '\u1433') ){ // '\u1433' U+1433 CANADIAN SYLLABICS PO
setChar('>');
*/
} else { // single char
//=========== TEST ==================
//System.out.println( command.charAt(0));
if (shiftEnabled == false) {
setChar(command.charAt(0));
} else { // command = char + index + array
int arrayIndex = Integer.parseInt("" + command.charAt(command.length() - 1));
int charIndex = Integer.parseInt(command.substring(1, command.length() - 1));
if (EN_TABLE[arrayIndex - 1][charIndex] == ' ') {
setChar( Character.toUpperCase(command.charAt(0)) );
} else {
setChar(EN_TABLE[arrayIndex - 1][charIndex]);
}
}
}
}
//=========== TEST ==================
public static void main(String[] args) {
JDialog dialog = new JDialog();
dialog.getContentPane().setLayout(new BoxLayout(dialog.getContentPane(), BoxLayout.Y_AXIS));
JButton butt = new JButton("TEST");
butt.setPreferredSize(new Dimension(300, 50));
dialog.add(butt);
JPasswordField psw = new JPasswordField();
psw.setPreferredSize(new Dimension (300,30));
dialog.add(psw);
//dialog.setPreferredSize(new Dimension (300,30));
dialog.setLocation(300,0);
//dialog.setSize(new Dimension(300, 400));
dialog.pack();
dialog.setVisible(true);
Keyboard kb = new Keyboard(dialog, psw);
kb.setVisible(true);
}
public AccessibleContext getAccessibleContext() {
if (accessibleContext == null) {
accessibleContext = new AccessibleKeyboard();
}
return accessibleContext;
}
protected class AccessibleKeyboard extends AccessibleJDialog {
//Inherit everything, override nothing.
}
}