diff --git a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java index 64c36a036..976465e90 100644 --- a/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/SingleValueDialog.java @@ -5,54 +5,204 @@ import de.neemann.digital.gui.sync.Sync; import de.neemann.digital.lang.Lang; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; import java.awt.*; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.Arrays; /** * Dialog to edit a single value. - * Used to enter a multi bit input value + * Used to enter a multi bit input value. * * @author hneemann + * @author Rüdiger Heintz */ public final class SingleValueDialog extends JDialog { - private String returnText; + private enum InMode { + HEX(Lang.get("attr_dialogHex")), + DECIMAL(Lang.get("attr_dialogDecimal")), + OCTAL(Lang.get("attr_dialogOctal")), + ASCII(Lang.get("attr_dialogAscii")), + HIGHZ(Lang.get("attr_dialogHighz")); - /** - * Creates a new instance - * - * @param pos the position to show the dialog - * @param text the text to edit - */ - private SingleValueDialog(Point pos, String text) { + private String langText; + + InMode(String langKey) { + this.langText = langKey; + } + + @Override + public String toString() { + return langText; + } + + public static InMode[] values(boolean supportsHighZ) { + if (supportsHighZ) { + return values(); + } else { + return Arrays.copyOf(values(), values().length - 1); + } + } + } + + private final JTextField textField; + private final boolean supportsHighZ; + private final JComboBox formatComboBox; + private JCheckBox[] checkBoxes; + private boolean programmaticModifyingFormat = false; + private long editValue; + private boolean ok = false; + + private SingleValueDialog(Point pos, ObservableValue value) { super((Frame) null, Lang.get("attr_dialogTitle"), true); setDefaultCloseOperation(DISPOSE_ON_CLOSE); - JTextField textField = new JTextField(30); - textField.setText(text); - getContentPane().add(textField); + editValue = value.getValue(); + supportsHighZ = value.supportsHighZ(); - textField.addActionListener(new ActionListener() { + textField = new JTextField(10); + textField.setHorizontalAlignment(JTextField.RIGHT); + formatComboBox = new JComboBox<>(InMode.values(supportsHighZ)); + formatComboBox.addActionListener(actionEvent -> { + if (!programmaticModifyingFormat) + setLongToDialog(editValue); + }); + + JPanel panel = new JPanel(new DialogLayout()); + panel.add(formatComboBox, DialogLayout.LABEL); + panel.add(textField, DialogLayout.INPUT); + panel.add(new JLabel(Lang.get("attr_dialogBinary")), DialogLayout.LABEL); + panel.add(createCheckBoxPanel(value.getBits(), editValue), DialogLayout.INPUT); + getContentPane().add(panel); + + textField.getDocument().addDocumentListener(new MyDocumentListener(() -> setStringToDialog(textField.getText()))); + + if (value.isHighZ()) + formatComboBox.setSelectedItem(InMode.HIGHZ); + else + setLongToDialog(editValue); + + JButton okButton = new JButton(new AbstractAction(Lang.get("ok")) { @Override - public void actionPerformed(ActionEvent e) { - returnText = textField.getText(); + public void actionPerformed(ActionEvent actionEvent) { + ok = true; dispose(); } }); + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.add(okButton); + getContentPane().add(buttonPanel, BorderLayout.EAST); + + getRootPane().setDefaultButton(okButton); + getRootPane().registerKeyboardAction(actionEvent -> dispose(), + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_IN_FOCUSED_WINDOW); pack(); setLocation(pos.x, pos.y); + textField.requestFocus(); + textField.select(0, Integer.MAX_VALUE); } - /** - * edits the given value - * - * @return result or null if dialog closed without something entered - */ - private String showDialog() { + private JPanel createCheckBoxPanel(int bits, long value) { + JPanel p = new JPanel(); + p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); + checkBoxes = new JCheckBox[bits]; + for (int i = bits - 1; i >= 0; i--) { + final int bit = i; + checkBoxes[bit] = new JCheckBox("", (value & (1L << bit)) != 0); + checkBoxes[bit].setBorder(null); + checkBoxes[bit].addActionListener(actionEvent -> setBit(bit, checkBoxes[bit].isSelected())); + p.add(checkBoxes[bit]); + } + return p; + } + + private void setBit(int bitNum, boolean set) { + if (set) + editValue |= 1L << bitNum; + else + editValue &= ~(1L << bitNum); + + if (getSelectedFormat().equals(InMode.HIGHZ)) + setSelectedFormat(InMode.HEX); + + setLongToDialog(editValue); + } + + private void setLongToDialog(long editValue) { + switch (getSelectedFormat()) { + case ASCII: + char val = (char) (editValue); + textField.setText("\'" + val + "\'"); + textField.setCaretPosition(1); + break; + case DECIMAL: + textField.setText(Long.toString(editValue)); + break; + case HEX: + textField.setText("0x" + Long.toHexString(editValue)); + break; + case OCTAL: + textField.setText("0" + Long.toOctalString(editValue)); + break; + case HIGHZ: + textField.setText("?"); + break; + default: + } + textField.requestFocus(); + } + + private InMode getSelectedFormat() { + return (InMode) formatComboBox.getSelectedItem(); + } + + private void setSelectedFormat(InMode format) { + if (!getSelectedFormat().equals(format)) { + programmaticModifyingFormat = true; + formatComboBox.setSelectedItem(format); + programmaticModifyingFormat = false; + } + } + + private void setStringToDialog(String text) { + text = text.trim(); + if (text.length() > 0) { + if (text.contains("?") && supportsHighZ) { + setSelectedFormat(InMode.HIGHZ); + editValue = 0; + } else if (text.charAt(0) == '\'') { + setSelectedFormat(InMode.ASCII); + if (text.length() > 1) { + editValue = text.charAt(1); + } else { + editValue = 0; + } + } else { + if (text.startsWith("0x")) + setSelectedFormat(InMode.HEX); + else if (text.startsWith("0")) + setSelectedFormat(InMode.OCTAL); + else + setSelectedFormat(InMode.DECIMAL); + try { + editValue = Long.decode(text); + } catch (NumberFormatException e) { + // do nothing on error + } + } + for (int i = 0; i < checkBoxes.length; i++) + checkBoxes[i].setSelected((editValue & (1L << i)) != 0); + } + } + + private boolean showDialog() { setVisible(true); - return returnText; + return ok; } /** @@ -62,23 +212,37 @@ public final class SingleValueDialog extends JDialog { * @param value the value to edit */ public static void editValue(Point pos, ObservableValue value, Sync modelSync) { - String ret = new SingleValueDialog(pos, value.getValueString()).showDialog(); - if (ret != null) { - ret = ret.trim(); - if (ret.equals("?") && value.supportsHighZ()) { - modelSync.access(() -> { - value.setHighZ(true); - }); + SingleValueDialog svd = new SingleValueDialog(pos, value); + if (svd.showDialog()) { + if (svd.getSelectedFormat().equals(InMode.HIGHZ)) { + modelSync.access(() -> value.setHighZ(true)); } else { - try { - long l = Long.decode(ret); - modelSync.access(() -> { - value.set(l, false); - }); - } catch (NumberFormatException e) { - - } + modelSync.access(() -> value.set(svd.editValue, false)); } } } + + + private static final class MyDocumentListener implements DocumentListener { + private final Runnable runnable; + + private MyDocumentListener(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void insertUpdate(DocumentEvent documentEvent) { + runnable.run(); + } + + @Override + public void removeUpdate(DocumentEvent documentEvent) { + runnable.run(); + } + + @Override + public void changedUpdate(DocumentEvent documentEvent) { + runnable.run(); + } + } } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index d20143412..1279b6d46 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -13,6 +13,12 @@ Öffnet die Schaltung in einem neuen Fenster. Hilfe Zeigt eine Beschreibung dieses Elements. + Hex + Dezimal + Ascii + HighZ + Oktal + Binär Verwerfen Bearbeiten Weiter bearbeiten diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 71316fe73..7f5ab5dd7 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -13,6 +13,12 @@ Opens the circuit in a new window. Help Shows a short description of this element. + Hex + Decimal + Ascii + HighZ + Octal + Binary Discard Changes Edit Continue editing