package mixconfig.panels.smartcard;
import groovy.swing.SwingBuilder;
import org.codehaus.groovy.binding.AggregateBinding;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.Dimension;
import javax.swing.JTabbedPane;
import net.miginfocom.swing.MigLayout;
import java.beans.PropertyChangeListener;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import java.awt.Color;
import javax.swing.JLabel;
import mixconfig.tools.dataretention.smartcard.ConnectionManager;
import mixconfig.tools.dataretention.smartcard.ISmartCard;
// TODO: Rename to SmartCardSettingsPanel
/**
* Installation
*/
abstract class InstallationWizard {
InstallationWizardFormModel model;
SwingBuilder swingBuilder;
JLabel errorMessages;
protected String actionLabel = 'XXX';
Map formElementsForModelProperties = [:]
public InstallationWizard() {
swingBuilder = new SwingBuilder();
}
public void show() {
def frame = swingBuilder.frame(title:'Installation Wizard', size:[300,300], pack: true, visible: true) {
panel(layout: new MigLayout("fill", "grow", "top")) {
errorMessages = label(text: "", constraints: 'wrap')
tabbedPane(constraints:"") {
panel(title: 'Administrator Settings', layout: new MigLayout()) {
label(text: "needed number of administrators")
textFieldConnectedToModel(targetProperty: 'neededNumberOfAdministrators', dataType: 'int', constraints: "wrap")
checkBoxConnectedToModel(text: 'Administrators can delete/create other administrators and change the "needed number of administrators" after initialization', targetProperty: "allowedToModifyAdministrators", constraints: "span")
userEditingAreaConnectedToModel(role: 'Administrator', targetProperty: 'administrators')
}
panel(title: 'Operator Settings', layout: new MigLayout()) {
label(text: "needed number of operators")
textFieldConnectedToModel(targetProperty: 'neededNumberOfOperators', dataType: 'int', constraints: "wrap")
checkBoxConnectedToModel(text: 'Administrators can delete/create operators and change the "needed number of operators" after initialization', targetProperty: "allowedToModifyOperators", constraints: "span")
userEditingAreaConnectedToModel(role: 'Operator', targetProperty: 'operators')
}
panel(title: 'Time Server Settings', layout: new MigLayout()) {
label(text: "needed number of time servers")
textFieldConnectedToModel(targetProperty: 'neededNumberOfTimeServers', dataType: 'int', constraints: "wrap")
checkBoxConnectedToModel(text: 'Administrators can change time servers and change the "needed number of time servers" after initialization', targetProperty: "allowedToModifyTimeServers", constraints: "span")
timeServerPanel(targetProperty: 'timeServers')
}
}
panel(constraints: "dock south") {
button(text:actionLabel,
actionPerformed: { save() }
)
}
}
}
frame.show()
}
abstract protected void save();
protected void validate() {
String errorMessageString = "
Errors
";
formElementsForModelProperties.each {model, propertyNameToTextBoxMapping ->
def validationErrors = model.validate()
propertyNameToTextBoxMapping.each { propertyName, textBox ->
if (validationErrors[propertyName]) {
textBox.background = new java.awt.Color(1f, 0f, 0f)
} else {
textBox.background = new java.awt.Color(1f, 1f, 1f)
}
}
validationErrors.each { propertyName, messages ->
messages.each { singleErrorMessage ->
errorMessageString += "- " + singleErrorMessage + "
"
}
}
}
errorMessageString += "
"
errorMessages.setText(errorMessageString);
}
/**
* model -> Model element. If empty, uses "InstallationWizardFormModel" this.model
* targetProperty -> name of target property in the model
* constraints -> constraints for the layout manager
* dataType -> if "int", then validation and conversion is done into an integer
*
*/
protected def textFieldConnectedToModel(Map params) {
def model = this.model
if (params.model) {
model = params.model
}
def textField = swingBuilder.textField(
constraints: params.constraints,
text: model[params.targetProperty],
columns: 10
);
def validator
def converter
if (params.dataType == 'int') {
// TODO: VALIDATION (nicely done!)
validator = {it.isInteger()}
converter = {it.toInteger()}
}
swingBuilder.bind(source: textField, sourceProperty: 'text',
target: model, targetProperty: params.targetProperty,
validator: validator, converter: converter
);
if (this.formElementsForModelProperties[model] == null) {
this.formElementsForModelProperties[model] = [:]
model.addPropertyChangeListener({evt -> validate() } as PropertyChangeListener)
}
this.formElementsForModelProperties[model][params.targetProperty] = textField
validate()
return textField
}
/**
* targetProperty -> name of target property in the model
* text -> Text to display next to the text box
* constraints -> constraints for the layout manager
*
*/
protected def checkBoxConnectedToModel(Map params) {
def checkBox = swingBuilder.checkBox(
constraints: params.constraints,
text: "" + params.text,
selected: model[params.targetProperty]
);
swingBuilder.bind(source: checkBox, sourceProperty: 'selected',
target: model, targetProperty: params.targetProperty
);
return checkBox
}
protected def timeServerPanel(Map params) {
// params.targetProperty
listingConnectedToModel(params.targetProperty, { timeServer ->
def swingElementsOfCurrentRow = []
swingElementsOfCurrentRow << swingBuilder.label(text: "Time Server")
swingElementsOfCurrentRow << textFieldConnectedToModel(model: timeServer, targetProperty: 'uri')
swingElementsOfCurrentRow << textFieldConnectedToModel(model: timeServer, targetProperty: 'certificate')
return swingElementsOfCurrentRow
},
{
return new TimeServer();
})
}
protected def userEditingAreaConnectedToModel(Map params) {
// params.role
// params.targetProperty
// params.constraints
listingConnectedToModel(
params.targetProperty,
// Now follows the rendering closure for rendering a single line
{ user ->
def swingElementsOfCurrentRow = []
swingElementsOfCurrentRow << swingBuilder.label(text: params.role + " ")
swingElementsOfCurrentRow << textFieldConnectedToModel(model: user, targetProperty: 'name')
swingElementsOfCurrentRow << swingBuilder.button(id: 'setPasswordButton', text: 'Set Password', actionPerformed: {
def password
def passwordConfirmation
def passwordContainer = swingBuilder.panel(layout: new MigLayout()){
swingBuilder.label(text: "Enter password for user \"${user.name}\"", constraints: "wrap")
swingBuilder.label(text: "Password")
password = swingBuilder.passwordField(columns: 10, echoChar: (char) '*', constraints: "wrap")
swingBuilder.label(text: "Confirm Password")
passwordConfirmation = swingBuilder.passwordField(columns: 10, echoChar: (char) '*')
}
JOptionPane.showMessageDialog(
null,
passwordContainer,
"Set " + params.role + " Password",
JOptionPane.OK_OPTION
);
if (password.getPassword() == passwordConfirmation.getPassword()) {
user.setPassword(new String(password.getPassword()))
} else {
JOptionPane.showMessageDialog(
null,
"The two passwords do not match. Please re-enter them.",
"Password Failure",
JOptionPane.OK_OPTION
)
}
})
return swingElementsOfCurrentRow
},
{
// This is the closure for adding a new element.
if (params.role == 'Operator') {
return new Operator();
} else {
return new Administrator();
}
}
)
}
/**
* targetProperty -> String, Property must be observable
*/
protected def listingConnectedToModel(targetProperty, renderingClosure, addNewElementClosure) {
buildListingOfModelElements(targetProperty, renderingClosure);
swingBuilder.button(text: 'Add new', actionPerformed: {
model[targetProperty] << addNewElementClosure()
})
}
protected void buildListingOfModelElements(targetProperty, renderingClosure) {
def swingElementList = []
def deleteButton = { propertyNameOfList, elementToRemove ->
return swingBuilder.button(constraints: "wrap", text: 'Delete', actionPerformed: {
model[propertyNameOfList].remove(elementToRemove)
formElementsForModelProperties.remove(elementToRemove)
validate()
})
}
// Build listing
//swingBuilder.scrollPane() {
def list = swingBuilder.panel(constraints: "span", layout: new MigLayout("fill", "")) {
model[targetProperty].eachWithIndex {element, index ->
def swingElementsOfCurrentRow = renderingClosure(element)
swingElementsOfCurrentRow << deleteButton(targetProperty, element)
swingElementList << swingElementsOfCurrentRow
}
}
//}
// Add property change listener
model[targetProperty].addPropertyChangeListener({evt ->
if (evt instanceof groovy.util.ObservableList.ElementRemovedEvent) {
def elementsToBeRemovedFromGui = swingElementList[evt.getIndex()]
elementsToBeRemovedFromGui.each { elementToBeRemovedFromGui ->
list.remove(elementToBeRemovedFromGui)
}
swingElementList.remove(elementsToBeRemovedFromGui)
list.revalidate()
} else if (evt instanceof groovy.util.ObservableList.ElementAddedEvent) {
swingBuilder.panel(list) {
def swingElementsOfCurrentRow = renderingClosure(evt.newValue)
swingElementsOfCurrentRow << deleteButton(targetProperty, evt.newValue)
swingElementList << swingElementsOfCurrentRow
}
list.revalidate()
}
} as PropertyChangeListener)
}
}
class SmartCardInitializationPanel extends InstallationWizard {
protected String actionLabel = 'Initialize Card!'
SmartCardInitializationPanel() {
super()
this.model = new InstallationWizardFormModel();
}
protected void save() {
model.saveToCard()
}
}
class SmartCardSettingsPanel extends InstallationWizard {
protected String actionLabel = 'Save changed settings to card!'
def unchangedModel = null;
SmartCardSettingsPanel() {
super()
model = new InstallationWizardFormModel()
ISmartCard smartCard = ConnectionManager.getInstance().connect();
model.loadFromCard(smartCard);
unchangedModel = model.clone()
}
protected void save() {
def changes = model.getChangesFrom(unchangedModel)
println changes
}
}