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

" 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 } }