/* * Copyright (c) 2002, 2003, 2009 Jochen Kupperschmidt * Released under the terms of the MIT License * _ _ * | |_ ___ _____ ___ _ _ _ ___ ___| |_ * | | . | | ._| | | | . | _| . / * |_|_|___|_|_|_|___|_____|___|_| |_|_\ * http://homework.nwsnet.de/ * * Convert numbers between binary, octal, decimal and hexadecimal. * * This is a Groovy remake (24-Dec-2009) of the original Java application * (12-Jan-2003) and requires significantly less code. */ import groovy.swing.SwingBuilder import java.awt.GridLayout import javax.swing.ButtonGroup import javax.swing.JFrame import javax.swing.JPanel import javax.swing.JRadioButton import javax.swing.JTextField import javax.swing.text.AbstractDocument import javax.swing.text.AttributeSet import javax.swing.text.BadLocationException import javax.swing.text.PlainDocument enum NumberSystem { BINARY(2, '01', { Integer.toBinaryString(it) }), OCTAL(8, '01234567', { Integer.toOctalString(it) }), DECIMAL(10, '0123456789', { Integer.toString(it) }), HEXADECIMAL(16, '0123456789abcdefABCDEF', { Integer.toHexString(it).toUpperCase() }) final int base final Set validCharacters final Closure toStringValue NumberSystem(int base, String validCharacters, Closure toStringValue) { this.base = base this.validCharacters = new HashSet(validCharacters.collect { it }) this.toStringValue = toStringValue } } class NumberDocument extends PlainDocument { private Set validCharacters private Closure actionHandler def NumberDocument(Set validCharacters, Closure actionHandler) { this.validCharacters = validCharacters this.actionHandler = actionHandler } // Fire action on text insertion. public void insertUpdate(AbstractDocument.DefaultDocumentEvent event, AttributeSet attrSet) { actionHandler() } // Fire action on text removal. public void postRemoveUpdate(AbstractDocument.DefaultDocumentEvent event) { actionHandler() } // Validate the entered characters. public void insertString(int offset, String str, AttributeSet attrSet) throws BadLocationException { super.insertString(offset, str.findAll { validCharacters.contains(it) }.join(''), attrSet) } } /** * A panel containing a labeled radio button and a special text field. */ class NumberPanel extends JPanel { NumberSystem system JRadioButton radioButton JTextField field def NumberPanel(NumberSystem system, boolean selected) { this.system = system layout = new GridLayout(0, 2) // Create a radio button. radioButton = new JRadioButton(selected: selected, text: system.name()[0] + system.name()[1..-1].toLowerCase() + ':') add(radioButton) // Create a text field that only accepts specified characters. field = new JTextField(document: new NumberDocument(system.validCharacters, { field.fireActionPerformed() }), enabled: selected, horizontalAlignment: JTextField.RIGHT) add(field) } def void changeInputSystem() { field.enabled = radioButton.selected } def void clear() { field.text = '' } def boolean isEmpty() { field.text.trim().isEmpty() } def int getValue() throws NumberFormatException { Integer.valueOf(field.text.trim(), system.base) } def void setValue(int value) { field.text = system.toStringValue(value) } } class NumberPanelGroup { def panels = [] ButtonGroup radioButtonGroup = new ButtonGroup() boolean locked = false def NumberPanelGroup(NumberSystem selectedSystem) { NumberSystem.values().each {system -> def numberPanel = new NumberPanel(system, system == selectedSystem) radioButtonGroup.add(numberPanel.radioButton) numberPanel.radioButton.actionPerformed = { panels*.changeInputSystem() } numberPanel.field.actionPerformed = { // Locking is needed since it would cause another // action event otherwise, resulting in a loop. if (locked) { return } locked = true try { if (!numberPanel.isEmpty()) { // Re-calculate values. panels*.value = numberPanel.value } else { // Clear all fields. panels*.clear() } } finally { locked = false } } panels << numberPanel } } } // Program starts here. def swing = new SwingBuilder() swing.lookAndFeel('gtk') swing.frame(defaultCloseOperation: JFrame.EXIT_ON_CLOSE, locationRelativeTo: null, pack: true, resizable: false, show: true, title: 'Number Converter') { panel(border: compoundBorder(emptyBorder(4), compoundBorder(etchedBorder(), emptyBorder(2)))) { gridLayout(cols: 1, rows: 0) new NumberPanelGroup(NumberSystem.DECIMAL).panels.each { widget(it) } } }