package hexenschach.gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Locale;
import javax.swing.JCheckBox;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;
import hexenschach.gui.event.GameEvent;
import hexenschach.gui.event.GameListener;
import hexenschach.gui.graphic.HexenschachPanel;
/**
* Panel, welches die Komponenten zur
* Tastatursteuerung des Spiels enthaelt.
* @author Tobias Marquardt
*/
class KeyboardControl extends JPanel implements GUIContainer, GameListener {
private static final long serialVersionUID = 7189643200168543836L;
private GUI gui;
// Größe des Panels
static final int xSize = 328;
static final int ySize = 150;
// Eingabefelder
private JFormattedTextField from;
private JFormattedTextField to;
// Checkbox
private JCheckBox labelDisplay;
// Beschriftung
private JLabel toLabel;
// Größe der Eingabefelder
static final int xSizeTextField = 60;
static final int ySizeTextField = 30;
// Speichert, ob eine gültige Felder in den Eingabefeldern stehen
private boolean correctTo;
private boolean correctFrom;
KeyboardControl(GUI gui) {
this.gui = gui;
correctTo = false;
correctFrom = false;
initLayout();
initComponents();
initContainerProperties();
initListener();
}
private void initListener() {
// Listener hinzufügen
from.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
fromFieldChanged();
}
@Override
public void insertUpdate(DocumentEvent e) {
fromFieldChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
fromFieldChanged();
}
});
to.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
toFieldChanged();
}
@Override
public void insertUpdate(DocumentEvent e) {
toFieldChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
toFieldChanged();
}
});
// Auf Tastatureingaben lauschen, um bei Druck auf Enter einen Spielzug zu machen.
to.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {}
@Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER & correctTo & correctFrom) {
// Feldauswahl ans Gameplay senden
gui.getGameplay().processFieldInput(to.getText());
from.setText("");
to.setText("");
}
}@Override
public void keyTyped(KeyEvent e) {}
});
// Der Checkbox einen ActionListener hinzufügen, um bei Änderung, den Hintergrund entsprechend zu ändern.
labelDisplay.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(labelDisplay.isSelected() == true)
gui.setLabeledBackground(true);
else
gui.setLabeledBackground(false);
}
});
}
public void initLayout() {
setLayout(null);
}
public void initComponents() {
// Initialisierung
from = new JFormattedTextField();
to = new JFormattedTextField();
// Farben
from.setBackground(new Color(170, 71, 12, 255));
to.setBackground(new Color(170, 71, 12, 255));
from.setForeground(new Color(236, 182, 74, 255));
to.setForeground(new Color(236, 182, 74, 255));
// Schriftart
from.setFont(new Font("Dialog", Font.BOLD, 20));
to.setFont(new Font("Dialog", Font.BOLD, 20));
// Größe
from.setPreferredSize(new Dimension(xSizeTextField, ySizeTextField));
to.setPreferredSize(new Dimension(xSizeTextField, ySizeTextField));
// Positionierung
from.setBounds(70, 50, xSizeTextField, ySizeTextField);
to.setBounds(190, 50, xSizeTextField, ySizeTextField);
/**
* Die Textfelder bekommen ein angepasstes Document, welches die Eingabe sinnvoll einschränkt.
* Sobald ein im Textfeld etwas neues angezeigt wird, tritt der Document-Listener in Kraft
* und ändert die Hintergrundfarbe je nach Korrektheit der Eingabe.
* Dies kann nicht direkt in dem Document passieren, da das Document sein Textfeld nicht kennt.
*/
// Eingabe-Beschränkung
from.setDocument(new LimitedSizeDocument());
to.setDocument(new LimitedSizeDocument());
// Deaktivierung zu Beginn
from.setEnabled(false);
to.setEnabled(false);
// Fokus setzen TODO
from.requestFocusInWindow();
// Hinzufügen
add(from);
add(to);
// Beschriftung setzen
toLabel = new JLabel(Dictionary.KeyBoardControlLabel);
toLabel.setBounds(140, 50, 60, 30);
toLabel.setOpaque(false);
toLabel.setForeground(new Color(236, 182, 74, 255));
toLabel.setFont(new Font("Dialog", Font.BOLD, 14));
add(toLabel);
// Checkbox, um Beschriftung des Spielfelds ein-/auszuschalten
labelDisplay = new JCheckBox(Dictionary.KeyBoardControlCheckBoxLabel, true);
labelDisplay.setBounds(70, 100, 300, 30);
labelDisplay.setOpaque(false);
labelDisplay.setForeground(new Color(236, 182, 74, 255));
labelDisplay.setFont(new Font("Dialog", Font.BOLD, 14));
labelDisplay.setSelected(true);
add(labelDisplay);
}
public void initContainerProperties() {
gui.addGameListener(this);
setOpaque(false); //Paneltransparenz
setBorder(null);
setPreferredSize(new Dimension(xSize, ySize));
}
/**
* Überprüft ob in dem "From"-Textfeld
* eine korrekte Feldbezeichnung steht
* und färbt das Feld entsprechend ein.
* @param field zu überprüfendes Text-Feld.
*/
private void fromFieldChanged() {
//Überprüfen ob die Eingabe ein korrektes Feld ergibt.
if(HexenschachPanel.isFieldNameValid(from.getText())) {
from.setBackground(new Color(100, 160, 12));
// Feldauswahl ans Gameplay senden
gui.getGameplay().processFieldInput(from.getText());
correctFrom = true;
}
else {
from.setBackground(new Color(170, 71, 12));
gui.resetHighlight();
correctFrom = false;
}
}
/**
* Überprüft ob in dem "To"-Textfeld
* eine korrekte Feldbezeichnung steht
* und färbt das Feld entsprechend ein.
* @param field zu überprüfendes Text-Feld.
*/
private void toFieldChanged() {
//Überprüfen ob die Eingabe ein korrektes Feld ergibt.
if(HexenschachPanel.isFieldNameValid(to.getText())) {
to.setBackground(new Color(100, 160, 12));
correctTo = true;
}
else {
to.setBackground(new Color(170, 71, 12));
correctTo = false;
}
}
/**
* Diese Klasse ist dazu nötig, um die Eingabe in den Textfeldern
* auf eine bestimmte Länge zu begrenzen.
* Dies geschieht, indem man dem JTextField mit setDocument() ein anderes Document
* zuweist.
* Zudem werden alle Kleinbuchstaben automatisch in Großbuchstabend umgewandelt und
* es wird sichergestellt, dass nur die erlaubten Buchstaben und Ziffern angenommen werden.
* Die Klasse ist ein solches Dokument, da sie von PlainDocument abgeleitet ist.
* Hier wird die Methode insertString() überschrieben, um bei der Eingabe in
* das Textfeld diese sofort validieren zu können.
* @author Tobias Marquardt
*/
class LimitedSizeDocument extends PlainDocument {
private static final long serialVersionUID = 8304338741426811325L;
// Begrenzte Zeichenanzahl
private int maxSize = 3;
/**
* Überschreibt die Methode aus PlainDocument.
* Wird aufgerufen, wenn Zeichen in das Textfeld, welches
* dieses Dokuemnt benutzt, eingegeben werden.
*/
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
// Wenn die maximale Länge nicht überschritten wird, füge den neuen String
// durch Aufruf der insertString()-Methode der Superklasse ein.
if((getLength() + str.length()) <= maxSize) {
// In Großbuchstaben umwandeln. Locale.ENGLISH um einheitliche Umwandlung zu garantieren.
str = str.toUpperCase(Locale.ENGLISH);
// Nur die erlaubten Zeichen aus dem String übernehmen
str = removeInvalidCharacters(str);
super.insertString(offs, str, a);
}
}
/**
* Entfernt alle Zeichen aus dem String, die bei der Zug-Eingabe
* keinen Sinn machen.
* Erlaubt sind also nur:
* A-U und 0-9
* @param str Der String aus dem die ungültigen Zeichen entfernt werden sollen
* @return der neue String
*/
private String removeInvalidCharacters(String str) {
// String in Array von char umwandeln, um jeden Buchstaben einzeln überprüfen zu können
char[] characters = str.toCharArray();
// StringBuffer dem alle gültigen Buchstaben aus dem Array hinzugefügt werden
StringBuffer sb = new StringBuffer();
for (int i = 0; i < characters.length; i++) {
// Nur Großbuchstaben von A bis U und Ziffern zulassen
if((characters[i] >= 65 && characters[i] <= 85) || (characters[i] >= 48 && characters[i] <= 57))
sb.append(characters[i]);
}
str = sb.toString();
return str;
}
}
@Override
public void gameFinished(GameEvent e) {
from.setEnabled(false);
to.setEnabled(false);
}
@Override
public void gameSaved(GameEvent e) {}
@Override
public void gameStarted(GameEvent e) {
from.setEnabled(true);
to.setEnabled(true);
}
}