diff --git a/src/main/java/de/neemann/digital/analyse/TruthTable.java b/src/main/java/de/neemann/digital/analyse/TruthTable.java index e865f7f7c..329d668ac 100644 --- a/src/main/java/de/neemann/digital/analyse/TruthTable.java +++ b/src/main/java/de/neemann/digital/analyse/TruthTable.java @@ -16,15 +16,17 @@ import de.neemann.digital.analyse.quinemc.BoolTable; import de.neemann.digital.analyse.quinemc.BoolTableByteArray; import de.neemann.digital.analyse.quinemc.ThreeStateValue; import de.neemann.digital.lang.Lang; +import de.neemann.digital.undo.Copyable; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; /** * The description of a truth table. */ -public class TruthTable { +public class TruthTable implements Copyable { private final ArrayList variables; private final ArrayList results; @@ -148,6 +150,29 @@ public class TruthTable { } } + private TruthTable(TruthTable truthTable) { + variables = new ArrayList<>(truthTable.variables.size()); + for (Variable v : truthTable.variables) + variables.add(new Variable(v.getIdentifier())); + results = new ArrayList<>(); + for (int i = 0; i < truthTable.getResultCount(); i++) { + Result result = truthTable.results.get(i); + addResult(result.getName(), new BoolTableByteArray(result.values)); + } + this.modelAnalyzerInfo = truthTable.modelAnalyzerInfo; + } + + /** + * Clears the table and sets the given variables + * + * @param vars the variables to use + */ + public void clear(Collection vars) { + variables.clear(); + variables.addAll(vars); + results.clear(); + } + /** * Returns the number of rows * @@ -489,6 +514,31 @@ public class TruthTable { return modelAnalyzerInfo; } + @Override + public TruthTable createDeepCopy() { + return new TruthTable(this); + } + + /** + * @return the names of all input variables + */ + public ArrayList getVarNames() { + ArrayList names = new ArrayList<>(); + for (Variable v : variables) + names.add(v.getIdentifier()); + return names; + } + + /** + * @return the names of al result variables + */ + public ArrayList getResultNames() { + ArrayList names = new ArrayList<>(); + for (Result r : results) + names.add(r.getName()); + return names; + } + /** * A single result column */ diff --git a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java index 0b8bc348b..c76925f13 100644 --- a/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java +++ b/src/main/java/de/neemann/digital/analyse/TruthTableTableModel.java @@ -6,6 +6,8 @@ package de.neemann.digital.analyse; import de.neemann.digital.analyse.quinemc.BoolTable; +import de.neemann.digital.undo.ModifyException; +import de.neemann.digital.undo.UndoManager; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; @@ -23,31 +25,31 @@ public class TruthTableTableModel implements TableModel { */ public static final String[] STATENAMES = new String[]{"0", "1", "x"}; - private final TruthTable truthTable; private final ArrayList listeners = new ArrayList<>(); + private final UndoManager undoManager; /** * Creates a new instance * - * @param truthTable the truthTable which is to visualize + * @param undoManager the undoManager */ - public TruthTableTableModel(TruthTable truthTable) { - this.truthTable = truthTable; + public TruthTableTableModel(UndoManager undoManager) { + this.undoManager = undoManager; } @Override public int getRowCount() { - return truthTable.getRows(); + return undoManager.getActual().getRows(); } @Override public int getColumnCount() { - return truthTable.getCols(); + return undoManager.getActual().getCols(); } @Override public String getColumnName(int columnIndex) { - return truthTable.getColumnName(columnIndex); + return undoManager.getActual().getColumnName(columnIndex); } @Override @@ -57,35 +59,52 @@ public class TruthTableTableModel implements TableModel { @Override public boolean isCellEditable(int rowIndex, int columnIndex) { - return truthTable.isEditable(columnIndex); + return undoManager.getActual().isEditable(columnIndex); } @Override public Object getValueAt(int rowIndex, int columnIndex) { - return truthTable.getValue(rowIndex, columnIndex); + return undoManager.getActual().getValue(rowIndex, columnIndex); } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { if (aValue instanceof Integer) - truthTable.setValue(rowIndex, columnIndex, (Integer) aValue); + setValue(rowIndex, columnIndex, (Integer) aValue); if (aValue instanceof String) { if (aValue.toString().equals("0")) - truthTable.setValue(rowIndex, columnIndex, 0); + setValue(rowIndex, columnIndex, 0); else if (aValue.toString().equals("1")) - truthTable.setValue(rowIndex, columnIndex, 1); + setValue(rowIndex, columnIndex, 1); else - truthTable.setValue(rowIndex, columnIndex, 2); + setValue(rowIndex, columnIndex, 2); } fireModelEvent(rowIndex); } + private void setValue(int rowIndex, int columnIndex, int val) { + try { + undoManager.apply(truthTable -> truthTable.setValue(rowIndex, columnIndex, val)); + } catch (ModifyException e) { + e.printStackTrace(); + } + } + private void fireModelEvent(int rowIndex) { TableModelEvent e = new TableModelEvent(this, rowIndex); for (TableModelListener l : listeners) l.tableChanged(e); } + /** + * Fires a structural table change + */ + public void fireTableChanged() { + TableModelEvent e = new TableModelEvent(this, HEADER_ROW); + for (TableModelListener l : listeners) + l.tableChanged(e); + } + @Override public void addTableModelListener(TableModelListener l) { listeners.add(l); @@ -95,13 +114,6 @@ public class TruthTableTableModel implements TableModel { public void removeTableModelListener(TableModelListener l) { } - /** - * @return the truth table used by this model - */ - public TruthTable getTable() { - return truthTable; - } - /** * Sets the column name * @@ -109,7 +121,11 @@ public class TruthTableTableModel implements TableModel { * @param name the new name */ public void setColumnName(int columnIndex, String name) { - truthTable.setColumnName(columnIndex, name); + try { + undoManager.apply(truthTable -> truthTable.setColumnName(columnIndex, name)); + } catch (ModifyException e) { + e.printStackTrace(); + } fireModelEvent(HEADER_ROW); } @@ -121,18 +137,26 @@ public class TruthTableTableModel implements TableModel { */ public void incValue(BoolTable boolTable, int row) { int col = -1; - for (int i = 0; i < truthTable.getResultCount(); i++) { - if (truthTable.getResult(i) == boolTable) { + TruthTable tt = undoManager.getActual(); + for (int i = 0; i < tt.getResultCount(); i++) { + if (tt.getResult(i) == boolTable) { col = i; break; } } if (col >= 0) { - col += truthTable.getVars().size(); - int value = truthTable.getValue(row, col); + col += tt.getVars().size(); + int value = tt.getValue(row, col); if (value == 2) value = 0; else value++; setValueAt(value, row, col); } } + + /** + * @return the truth table shown + */ + public TruthTable getTable() { + return undoManager.getActual(); + } } diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/BoolTableByteArray.java b/src/main/java/de/neemann/digital/analyse/quinemc/BoolTableByteArray.java index 3228e05a5..5ed3f589d 100644 --- a/src/main/java/de/neemann/digital/analyse/quinemc/BoolTableByteArray.java +++ b/src/main/java/de/neemann/digital/analyse/quinemc/BoolTableByteArray.java @@ -32,6 +32,17 @@ public class BoolTableByteArray implements BoolTable { this.table = table; } + /** + * Creates a new instance + * + * @param values the values to initialize the table + */ + public BoolTableByteArray(BoolTable values) { + table = new byte[values.size()]; + for (int i = 0; i < values.size(); i++) + table[i] = (byte) values.get(i).asInt(); + } + @Override public int size() { return table.length; diff --git a/src/main/java/de/neemann/digital/gui/components/table/LaTeXExpressionListener.java b/src/main/java/de/neemann/digital/gui/components/table/LaTeXExpressionListener.java index 001182e0a..883680621 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/LaTeXExpressionListener.java +++ b/src/main/java/de/neemann/digital/gui/components/table/LaTeXExpressionListener.java @@ -5,7 +5,7 @@ */ package de.neemann.digital.gui.components.table; -import de.neemann.digital.analyse.TruthTableTableModel; +import de.neemann.digital.analyse.TruthTable; import de.neemann.digital.analyse.expression.Expression; import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.NamedExpression; @@ -15,10 +15,10 @@ import de.neemann.digital.draw.graphics.text.formatter.LaTeXFormatter; final class LaTeXExpressionListener implements ExpressionListener { private final StringBuilder sb; - LaTeXExpressionListener(TruthTableTableModel model) throws ExpressionException { + LaTeXExpressionListener(TruthTable truthTable) throws ExpressionException { sb = new StringBuilder(); - if (model.getTable().getRows() <= 256) { - String text = new TruthTableFormatterLaTeX().format(model.getTable()); + if (truthTable.getRows() <= 256) { + String text = new TruthTableFormatterLaTeX().format(truthTable); sb.append(text); } sb.append("\\begin{eqnarray*}\n"); diff --git a/src/main/java/de/neemann/digital/gui/components/table/ProgressDialog.java b/src/main/java/de/neemann/digital/gui/components/table/ProgressDialog.java index 957ba956c..a12a9b42b 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/ProgressDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/ProgressDialog.java @@ -27,7 +27,7 @@ public class ProgressDialog extends JDialog implements ExpressionCreator.Progres super(tableDialog, false); setDefaultCloseOperation(DISPOSE_ON_CLOSE); - bar = new JProgressBar(0, tableDialog.getModel().getTable().getResultCount()); + bar = new JProgressBar(0, tableDialog.getUndoManager().getActual().getResultCount()); int b = Screen.getInstance().getFontSize(); bar.setBorder(BorderFactory.createEmptyBorder(b, b, b, b)); final JLabel label = new JLabel(Lang.get("msg_optimizationInProgress")); diff --git a/src/main/java/de/neemann/digital/gui/components/table/ReorderInputs.java b/src/main/java/de/neemann/digital/gui/components/table/ReorderInputs.java index dc01a4a0f..80381ec96 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/ReorderInputs.java +++ b/src/main/java/de/neemann/digital/gui/components/table/ReorderInputs.java @@ -55,10 +55,9 @@ public class ReorderInputs { /** * Creates a new table matching the actual state of the items * - * @return the new table * @throws ExpressionException ExpressionException */ - public TruthTable reorder() throws ExpressionException { + public void reorder() throws ExpressionException { ArrayList newVars = new ArrayList<>(); ArrayList deletedVars = new ArrayList<>(table.getVars()); @@ -79,21 +78,20 @@ public class ReorderInputs { if (newVars.size() < 2) throw new ExpressionException(Lang.get("err_tableBecomesToSmall")); - TruthTable newTable = new TruthTable(newVars); - newTable.setModelAnalyzerInfo(table.getModelAnalyzerInfo()); - for (int j = 0; j < table.getResultCount(); j++) - newTable.addResult(table.getResultName(j)); + TruthTable oldTable = this.table.createDeepCopy(); - ContextFiller fc = new ContextFiller(newTable.getVars()); + table.clear(newVars); + for (int j = 0; j < oldTable.getResultCount(); j++) + table.addResult(oldTable.getResultName(j)); + + ContextFiller fc = new ContextFiller(table.getVars()); for (Variable v : deletedVars) fc.set(v, false); - for (int row = 0; row < newTable.getRows(); row++) { + for (int row = 0; row < table.getRows(); row++) { fc.setContextTo(row); - for (int t = 0; t < newTable.getResultCount(); t++) - newTable.setByContext(t, fc, table.getByContext(t, fc)); + for (int t = 0; t < table.getResultCount(); t++) + table.setByContext(t, fc, oldTable.getByContext(t, fc)); } - - return newTable; } } diff --git a/src/main/java/de/neemann/digital/gui/components/table/ReorderOutputs.java b/src/main/java/de/neemann/digital/gui/components/table/ReorderOutputs.java index 1f45585a6..ecb17b8c3 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/ReorderOutputs.java +++ b/src/main/java/de/neemann/digital/gui/components/table/ReorderOutputs.java @@ -53,23 +53,20 @@ public class ReorderOutputs { /** * Creates a new table matching the actual state of the items * - * @return the new table * @throws ExpressionException ExpressionException */ - public TruthTable reorder() throws ExpressionException { - TruthTable newTable = new TruthTable(table.getVars()); - newTable.setModelAnalyzerInfo(table.getModelAnalyzerInfo()); + public void reorder() throws ExpressionException { + TruthTable oldTable = table.createDeepCopy(); + table.clear(oldTable.getVars()); for (String name : names) { - for (int i = 0; i < table.getResultCount(); i++) - if (table.getResultName(i).equals(name)) { - newTable.addResult(table.getResultName(i), table.getResult(i)); + for (int i = 0; i < oldTable.getResultCount(); i++) + if (oldTable.getResultName(i).equals(name)) { + table.addResult(oldTable.getResultName(i), oldTable.getResult(i)); break; } } - if (newTable.getResultCount() < 1) + if (table.getResultCount() < 1) throw new ExpressionException(Lang.get("err_oneResultIsRequired")); - - return newTable; } } diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java index 7d4a42c01..fb0ff162d 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableDialog.java @@ -41,6 +41,8 @@ import de.neemann.digital.gui.components.table.hardware.GenerateCUPL; import de.neemann.digital.gui.components.table.hardware.GenerateFile; import de.neemann.digital.gui.components.table.hardware.HardwareDescriptionGenerator; import de.neemann.digital.lang.Lang; +import de.neemann.digital.undo.ModifyException; +import de.neemann.digital.undo.UndoManager; import de.neemann.gui.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,7 +68,7 @@ import java.util.prefs.Preferences; import static de.neemann.digital.analyse.ModelAnalyser.addOne; /** - * + * The dialog used to show the truth table. */ public class TableDialog extends JDialog { private static final Logger LOGGER = LoggerFactory.getLogger(TableDialog.class); @@ -102,15 +104,16 @@ public class TableDialog extends JDialog { private final ToolTipAction karnaughMenuAction; private final HashMap availGenerators = new HashMap<>(); private final JMenu hardwareMenu; + private final TruthTableTableModel model; private JCheckBoxMenuItem createJK; private File filename; - private TruthTableTableModel model; private int columnIndex; private AllSolutionsDialog allSolutionsDialog; private ExpressionListenerStore lastGeneratedExpressions; private KarnaughMapDialog kvMap; private JMenuItem lastUsedGenratorMenuItem; private Mouse mouse = Mouse.getMouse(); + private UndoManager undoManager; /** * Creates a new instance @@ -122,10 +125,15 @@ public class TableDialog extends JDialog { */ public TableDialog(Window parent, TruthTable truthTable, ElementLibrary library, File filename) { super(parent, Lang.get("win_table")); + undoManager = new UndoManager<>(truthTable); this.library = library; this.shapeFactory = library.getShapeFactory(); this.filename = filename; setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + model = new TruthTableTableModel(undoManager); + model.addTableModelListener(new CalculationTableModelListener()); + kvMap = new KarnaughMapDialog(this, (boolTable, row) -> model.incValue(boolTable, row)); statusBar = new ExpressionComponent(); @@ -182,63 +190,90 @@ public class TableDialog extends JDialog { } bar.add(sizeMenu); - JMenu columnsMenu = new JMenu(Lang.get("menu_table_columns")); - bar.add(columnsMenu); - columnsMenu.add(new ToolTipAction(Lang.get("menu_table_reorder_inputs")) { + JMenu edit = new JMenu(Lang.get("menu_edit")); + bar.add(edit); + + addUndoRedo(edit); + + edit.addSeparator(); + + edit.add(new ToolTipAction(Lang.get("menu_table_reorder_inputs")) { @Override public void actionPerformed(ActionEvent e) { - ReorderInputs ri = new ReorderInputs(model.getTable()); - if (new ElementOrderer<>(TableDialog.this, Lang.get("menu_table_reorder_inputs"), ri.getItems()) + ArrayList varNames = undoManager.getActual().getVarNames(); + if (new ElementOrderer<>(TableDialog.this, Lang.get("menu_table_reorder_inputs"), new ElementOrderer.ListOrder<>(varNames)) .addDeleteButton() .addOkButton() .showDialog()) { + try { - setModel(new TruthTableTableModel(ri.reorder())); - } catch (ExpressionException e1) { + undoManager.apply(tt -> { + try { + new ReorderInputs(tt, varNames).reorder(); + tableChanged(); + } catch (ExpressionException ex) { + throw new ModifyException("failed to reorder", ex); + } + }); + } catch (ModifyException e1) { new ErrorMessage().addCause(e1).show(TableDialog.this); } } } }.createJMenuItem()); - columnsMenu.add(new ToolTipAction(Lang.get("menu_table_columnsAddVariable")) { + edit.add(new ToolTipAction(Lang.get("menu_table_columnsAddVariable")) { @Override public void actionPerformed(ActionEvent actionEvent) { - TruthTable t = model.getTable(); - t.addVariable(); - setModel(new TruthTableTableModel(t)); + try { + undoManager.apply(TruthTable::addVariable); + tableChanged(); + } catch (ModifyException e) { + new ErrorMessage().addCause(e).show(TableDialog.this); + } } }.setToolTip(Lang.get("menu_table_columnsAddVariable_tt")).createJMenuItem()); - columnsMenu.add(new ToolTipAction(Lang.get("menu_table_reorder_outputs")) { + edit.add(new ToolTipAction(Lang.get("menu_table_reorder_outputs")) { @Override public void actionPerformed(ActionEvent e) { - ReorderOutputs ro = new ReorderOutputs(model.getTable()); - if (new ElementOrderer<>(TableDialog.this, Lang.get("menu_table_reorder_outputs"), ro.getItems()) + ArrayList resultNames = undoManager.getActual().getResultNames(); + if (new ElementOrderer<>(TableDialog.this, Lang.get("menu_table_reorder_outputs"), new ElementOrderer.ListOrder<>(resultNames)) .addDeleteButton() .addOkButton() .showDialog()) { try { - setModel(new TruthTableTableModel(ro.reorder())); - } catch (ExpressionException e1) { + undoManager.apply(tt -> { + try { + new ReorderOutputs(tt, resultNames).reorder(); + tableChanged(); + } catch (ExpressionException ex) { + throw new ModifyException("failed to reorder", ex); + } + }); + } catch (ModifyException e1) { new ErrorMessage().addCause(e1).show(TableDialog.this); } } } }.createJMenuItem()); - columnsMenu.add(new ToolTipAction(Lang.get("menu_table_columnsAdd")) { + edit.add(new ToolTipAction(Lang.get("menu_table_columnsAdd")) { @Override public void actionPerformed(ActionEvent actionEvent) { - TruthTable t = model.getTable(); - t.addResult(); - setModel(new TruthTableTableModel(t)); + try { + undoManager.apply(TruthTable::addResult); + tableChanged(); + } catch (ModifyException e) { + new ErrorMessage().addCause(e).show(TableDialog.this); + } + } }.setToolTip(Lang.get("menu_table_columnsAdd_tt")).createJMenuItem()); - bar.add(columnsMenu); + edit.addSeparator(); - bar.add(createSetMenu()); + createSetMenuEntries(edit); hardwareMenu = createCreateMenu(); bar.add(hardwareMenu); @@ -256,14 +291,55 @@ public class TableDialog extends JDialog { setJMenuBar(bar); - setModel(new TruthTableTableModel(truthTable)); - getContentPane().add(new JScrollPane(table)); getContentPane().add(statusBar, BorderLayout.SOUTH); pack(); setLocationRelativeTo(parent); } + private void addUndoRedo(JMenu edit) { + final ToolTipAction undo = new ToolTipAction(Lang.get("menu_undo")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + if (undoManager.undoAvailable()) { + try { + undoManager.undo(); + tableChanged(); + } catch (ModifyException e) { + new ErrorMessage().addCause(e).show(TableDialog.this); + } + } + } + }.setAcceleratorCTRLplus("Z"); + final ToolTipAction redo = new ToolTipAction(Lang.get("menu_redo")) { + @Override + public void actionPerformed(ActionEvent actionEvent) { + if (undoManager.redoAvailable()) { + try { + undoManager.redo(); + tableChanged(); + } catch (ModifyException e) { + new ErrorMessage().addCause(e).show(TableDialog.this); + } + } + } + }.setAcceleratorCTRLplus("Y"); + edit.add(undo.createJMenuItem()); + edit.add(redo.createJMenuItem()); + undoManager.addListener(() -> { + undo.setEnabled(undoManager.undoAvailable()); + redo.setEnabled(undoManager.redoAvailable()); + }); + undo.setEnabled(undoManager.undoAvailable()); + redo.setEnabled(undoManager.redoAvailable()); + } + + void tableChanged() { + karnaughMenuAction.setEnabled(undoManager.getActual().getVars().size() <= 4); + calculateExpressions(); + model.fireTableChanged(); + } + private void editColumnName(int columnIndex, Point pos) { ElementAttributes attr = new ElementAttributes(); final String name = model.getColumnName(columnIndex); @@ -290,7 +366,8 @@ public class TableDialog extends JDialog { try { File file = fc.getSelectedFile(); TruthTable truthTable = TruthTable.readFromFile(file); - setModel(new TruthTableTableModel(truthTable)); + undoManager.setInitial(truthTable); + tableChanged(); TableDialog.this.filename = file; } catch (IOException e1) { new ErrorMessage().addCause(e1).show(TableDialog.this); @@ -308,7 +385,7 @@ public class TableDialog extends JDialog { fc.setFileFilter(new FileNameExtensionFilter(Lang.get("msg_truthTable"), "tru")); new SaveAsHelper(TableDialog.this, fc, "tru").checkOverwrite( file -> { - model.getTable().save(file); + undoManager.getActual().save(file); TableDialog.this.filename = file; } ); @@ -320,7 +397,7 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { try { - final LaTeXExpressionListener laTeXExpressionListener = new LaTeXExpressionListener(model); + final LaTeXExpressionListener laTeXExpressionListener = new LaTeXExpressionListener(undoManager.getActual()); ExpressionListener expressionListener = laTeXExpressionListener; if (createJK.isSelected()) expressionListener = new ExpressionListenerJK(expressionListener); @@ -339,14 +416,14 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { int res = JOptionPane.OK_OPTION; - if (model.getTable().getVars().size() > 20) + if (undoManager.getActual().getVars().size() > 20) res = JOptionPane.showConfirmDialog(TableDialog.this, Lang.get("msg_tableHasManyRowsConfirm")); if (res == JOptionPane.OK_OPTION) { JFileChooser fc = new MyFileChooser(); if (TableDialog.this.filename != null) fc.setSelectedFile(SaveAsHelper.checkSuffix(TableDialog.this.filename, "hex")); new SaveAsHelper(TableDialog.this, fc, "hex") - .checkOverwrite(file -> model.getTable().saveHex(file)); + .checkOverwrite(file -> undoManager.getActual().saveHex(file)); } } }.setToolTip(Lang.get("menu_table_exportHex_tt")).createJMenuItem()); @@ -361,49 +438,53 @@ public class TableDialog extends JDialog { return fileMenu; } - private JMenu createSetMenu() { - JMenu setMenu = new JMenu(Lang.get("menu_table_set")); - setMenu.add(new ToolTipAction(Lang.get("menu_table_setXTo0")) { + private void createSetMenuEntries(JMenu edit) { + edit.add(new ToolTipAction(Lang.get("menu_table_setXTo0")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> v > 1 ? 0 : v); } }.setToolTip(Lang.get("menu_table_setXTo0_tt")).createJMenuItem()); - setMenu.add(new ToolTipAction(Lang.get("menu_table_setXTo1")) { + edit.add(new ToolTipAction(Lang.get("menu_table_setXTo1")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> v > 1 ? 1 : v); } }.setToolTip(Lang.get("menu_table_setXTo1_tt")).createJMenuItem()); - setMenu.add(new ToolTipAction(Lang.get("menu_table_setAllToX")) { + edit.add(new ToolTipAction(Lang.get("menu_table_setAllToX")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> (byte) 2); } }.setToolTip(Lang.get("menu_table_setAllToX_tt")).createJMenuItem()); - setMenu.add(new ToolTipAction(Lang.get("menu_table_setAllTo0")) { + edit.add(new ToolTipAction(Lang.get("menu_table_setAllTo0")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> (byte) 0); } }.setToolTip(Lang.get("menu_table_setAllTo0_tt")).createJMenuItem()); - setMenu.add(new ToolTipAction(Lang.get("menu_table_setAllTo1")) { + edit.add(new ToolTipAction(Lang.get("menu_table_setAllTo1")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> (byte) 1); } }.setToolTip(Lang.get("menu_table_setAllTo1_tt")).createJMenuItem()); - setMenu.add(new ToolTipAction(Lang.get("menu_table_invert")) { + edit.add(new ToolTipAction(Lang.get("menu_table_invert")) { @Override public void actionPerformed(ActionEvent e) { modifyTable(v -> v > 1 ? v : (byte) (1 - v)); } }.setToolTip(Lang.get("menu_table_invert_tt")).createJMenuItem()); - return setMenu; } private void modifyTable(BoolTableByteArray.TableModifier m) { - setModel(new TruthTableTableModel(model.getTable().modifyValues(m))); + try { + undoManager.apply(truthTable -> truthTable.modifyValues(m)); + tableChanged(); + } catch (ModifyException e) { + e.printStackTrace(); + new ErrorMessage().addCause(e).show(TableDialog.this); + } } private JMenu createCreateMenu() { @@ -527,8 +608,8 @@ public class TableDialog extends JDialog { private void createCircuit(boolean useJKff, boolean useLUTs, ExpressionModifier... modifier) { try { - final ModelAnalyserInfo modelAnalyzerInfo = model.getTable().getModelAnalyzerInfo(); - CircuitBuilder circuitBuilder = new CircuitBuilder(shapeFactory, model.getTable().getVars()) + final ModelAnalyserInfo modelAnalyzerInfo = undoManager.getActual().getModelAnalyzerInfo(); + CircuitBuilder circuitBuilder = new CircuitBuilder(shapeFactory, undoManager.getActual().getVars()) .setUseJK(useJKff) .setUseLUTs(useLUTs) .setModelAnalyzerInfo(modelAnalyzerInfo); @@ -555,19 +636,6 @@ public class TableDialog extends JDialog { return model; } - /** - * Sets the table model - * - * @param model the model to use - */ - public void setModel(TruthTableTableModel model) { - this.model = model; - model.addTableModelListener(new CalculationTableModelListener()); - table.setModel(model); - karnaughMenuAction.setEnabled(model.getTable().getVars().size() <= 4); - calculateExpressions(); - } - private String getProjectName() { if (filename == null) return "unknown"; @@ -598,6 +666,13 @@ public class TableDialog extends JDialog { } } + /** + * @return the undoManager + */ + public UndoManager getUndoManager() { + return undoManager; + } + private class CalculationTableModelListener implements TableModelListener { @Override public void tableChanged(TableModelEvent tableModelEvent) { @@ -613,7 +688,7 @@ public class TableDialog extends JDialog { if (createJK.isSelected()) expressionListener = new ExpressionListenerJK(expressionListener); - final TruthTable table = model.getTable(); + final TruthTable table = undoManager.getActual(); if (table.getVars().size() >= 8) { ProgressDialog progress = new ProgressDialog(this); @@ -674,7 +749,8 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent actionEvent) { - setModel(new TruthTableTableModel(new TruthTable(n).addResult())); + undoManager.setInitial(new TruthTable(n).addResult()); + tableChanged(); } } @@ -702,7 +778,8 @@ public class TableDialog extends JDialog { i--; } - setModel(new TruthTableTableModel(truthTable)); + undoManager.setInitial(truthTable); + tableChanged(); } } @@ -735,7 +812,8 @@ public class TableDialog extends JDialog { i--; } - setModel(new TruthTableTableModel(truthTable)); + undoManager.setInitial(truthTable); + tableChanged(); } } @@ -746,7 +824,7 @@ public class TableDialog extends JDialog { JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); label.setHorizontalAlignment(SwingConstants.CENTER); label.setFont(font); - if (column < model.getTable().getVars().size()) + if (column < undoManager.getActual().getVars().size()) label.setBackground(MYGRAY); else label.setBackground(Color.WHITE); @@ -761,7 +839,7 @@ public class TableDialog extends JDialog { } } - private final class StringDefaultTableCellRenderer extends DefaultTableCellRenderer { + private static final class StringDefaultTableCellRenderer extends DefaultTableCellRenderer { private StringDefaultTableCellRenderer() { setHorizontalAlignment(SwingConstants.CENTER); } @@ -832,13 +910,13 @@ public class TableDialog extends JDialog { int c = table.getSelectedColumn(); if (r < 0 || c < 0) { r = 0; - c = model.getTable().getVars().size(); + c = undoManager.getActual().getVars().size(); } model.setValueAt(value, r, c); c++; if (c >= table.getColumnCount()) { - c = model.getTable().getVars().size(); + c = undoManager.getActual().getVars().size(); r++; if (r >= model.getRowCount()) r = 0; @@ -859,7 +937,7 @@ public class TableDialog extends JDialog { @Override public void actionPerformed(ActionEvent e) { try { - generator.generate(TableDialog.this, filename, model.getTable(), lastGeneratedExpressions); + generator.generate(TableDialog.this, filename, undoManager.getActual(), lastGeneratedExpressions); setLastUsedGenerator(generator); } catch (Exception e1) { new ErrorMessage(Lang.get("msg_errorDuringHardwareExport")).addCause(e1).show(TableDialog.this); diff --git a/src/main/java/de/neemann/digital/gui/components/table/TableReorderManager.java b/src/main/java/de/neemann/digital/gui/components/table/TableReorderManager.java index 18c1b4d26..a5dda9198 100644 --- a/src/main/java/de/neemann/digital/gui/components/table/TableReorderManager.java +++ b/src/main/java/de/neemann/digital/gui/components/table/TableReorderManager.java @@ -10,6 +10,7 @@ import de.neemann.digital.analyse.TruthTableTableModel; import de.neemann.digital.analyse.expression.ExpressionException; import de.neemann.digital.analyse.expression.Variable; import de.neemann.digital.analyse.quinemc.BoolTable; +import de.neemann.digital.undo.ModifyException; import javax.swing.*; import javax.swing.table.TableModel; @@ -20,7 +21,7 @@ import java.util.ArrayList; /** * Handles reordering of the table columns by mouse drag and drop */ -public class TableReorderManager { +class TableReorderManager { private final TableDialog tableDialog; private final JTable table; @@ -31,7 +32,7 @@ public class TableReorderManager { * @param tableDialog the TableDialog instance * @param table the table which is reordered */ - public TableReorderManager(TableDialog tableDialog, JTable table) { + TableReorderManager(TableDialog tableDialog, JTable table) { this.tableDialog = tableDialog; this.table = table; table.getTableHeader().addMouseListener(new MouseAdapter() { @@ -64,24 +65,32 @@ public class TableReorderManager { } if (wasChange) { - if (isVarChange(varList, vars)) { - try { - TruthTable tt = new ReorderInputs(model.getTable(), vars).reorder(); - tableDialog.setModel(new TruthTableTableModel(tt)); - } catch (ExpressionException e) { - // can't happen because no columns are removed - e.printStackTrace(); - } - } else if (isResultChange(model.getTable(), results)) { - try { - TruthTable tt = new ReorderOutputs(model.getTable(), results).reorder(); - tableDialog.setModel(new TruthTableTableModel(tt)); - } catch (ExpressionException e) { - // can't happen because no columns are removed - e.printStackTrace(); - } - } else - tableDialog.setModel(new TruthTableTableModel(model.getTable())); + try { + if (isVarChange(varList, vars)) { + tableDialog.getUndoManager().apply(tt -> { + try { + new ReorderInputs(tt, vars).reorder(); + } catch (ExpressionException e) { + // can't happen because no columns are removed + e.printStackTrace(); + } + }); + tableDialog.tableChanged(); + } else if (isResultChange(model.getTable(), results)) { + tableDialog.getUndoManager().apply(tt -> { + try { + new ReorderOutputs(tt, results).reorder(); + } catch (ExpressionException e) { + // can't happen because no columns are removed + e.printStackTrace(); + } + }); + tableDialog.tableChanged(); + } else + tableDialog.tableChanged(); + } catch (ModifyException e) { + e.printStackTrace(); + } } } diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index 61f12aff5..7cdafb23f 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -1546,14 +1546,12 @@ Sind evtl. die Namen der Variablen nicht eindeutig? Kombinatorisch Automat Automat bidirektional - Spalten Eingangsvariablen umsortieren/löschen Ergebnisspalten umsortieren/löschen Ergebnisspalte hinzufügen Fügt der Tabelle eine Ergebnisspalte hinzu. Eingangsvariable hinzufügen Fügt der Tabelle eine Variablenspalte hinzu. - Setzen Setze X auf 0 Setzt die Don't Cares auf 0. Setze X auf 1 diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index 786738aae..641eabc14 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -1529,14 +1529,12 @@ Combinatorial Sequential Sequential bidirectional - Columns Reorder/Delete Input Variables Add Input Variable Adds a new input variable to the table. Reorder/Delete Output Columns Add Output Column Adds a new result column to the table. - Set Set X to 0 Sets the Don't Cares to 0. Set X to 1 diff --git a/src/main/resources/lang/lang_es.xml b/src/main/resources/lang/lang_es.xml index 1efeb74ea..2efe3dda6 100644 --- a/src/main/resources/lang/lang_es.xml +++ b/src/main/resources/lang/lang_es.xml @@ -1293,14 +1293,12 @@ Combinacional Secuencial Secuencial bidireccional - Columnas Reordena/Borra variables de entrada Reordena/Borra columnas de salida Añade columna de salida Añade una nueva columna de resultado a la tabla. Añade variable de entrada Añade una nueva variable de entrada a la tabla. - Fijar Fijar X a 0 Convierte los "indiferentes" a 0. Poner X a 1 diff --git a/src/main/resources/lang/lang_es_ref.xml b/src/main/resources/lang/lang_es_ref.xml index bb485ee62..c825aa2b4 100644 --- a/src/main/resources/lang/lang_es_ref.xml +++ b/src/main/resources/lang/lang_es_ref.xml @@ -1306,14 +1306,12 @@ Combinatorial Sequential Sequential bidirectional - Columns Reorder/Delete Input Variables Reorder/Delete Output Columns Add Output Column Adds a new result column to the table. Add Input Variable Adds a new input variable to the table. - Set Set X to 0 Sets the Don't Cares to 0. Set X to 1 diff --git a/src/main/resources/lang/lang_pt.xml b/src/main/resources/lang/lang_pt.xml index 65f3725d8..6dbb6e475 100644 --- a/src/main/resources/lang/lang_pt.xml +++ b/src/main/resources/lang/lang_pt.xml @@ -1362,14 +1362,12 @@ Combinacional Sequencial Sequencial bidirecional - Colunas Reordenar/remover variáveis de entrada Adicionar variável de entrada Adicionar uma nova variável de entrada à tabela. Reordenar/remover colunas de saída Adicionar coluna de saída Adicionar uma nova coluna de resultado à tabela. - Definir Definir X como 0 Definir "don't cares" como 0. Definir X como 1 diff --git a/src/main/resources/lang/lang_pt_ref.xml b/src/main/resources/lang/lang_pt_ref.xml index fc8eccb24..df378a19f 100644 --- a/src/main/resources/lang/lang_pt_ref.xml +++ b/src/main/resources/lang/lang_pt_ref.xml @@ -1373,14 +1373,12 @@ Combinatorial Sequential Sequential bidirectional - Columns Reorder/Delete Input Variables Add Input Variable Adds a new input variable to the table. Reorder/Delete Output Columns Add Output Column Adds a new result column to the table. - Set Set X to 0 Sets the Don't Cares to 0. Set X to 1 diff --git a/src/test/java/de/neemann/digital/docu/ScreenShots.java b/src/test/java/de/neemann/digital/docu/ScreenShots.java index 21fa9a3b4..14438be4c 100644 --- a/src/test/java/de/neemann/digital/docu/ScreenShots.java +++ b/src/test/java/de/neemann/digital/docu/ScreenShots.java @@ -159,6 +159,7 @@ public class ScreenShots { .add(new GuiTester.CloseTopMost()) .execute(); + /* File trafficLight = new File(Resources.getRoot(), "../../main/fsm/trafficLightBlink.fsm"); new GuiTester() .press("F10") @@ -217,7 +218,7 @@ public class ScreenShots { .add(new GuiTester.CloseTopMost()) .add(new GuiTester.CloseTopMost()) .add(new GuiTester.CloseTopMost()) - .execute();/**/ + .execute();*/ } private static GuiTester.WindowCheck closeAllSolutionsDialog() { diff --git a/src/test/java/de/neemann/digital/gui/components/table/LaTeXExpressionListenerTest.java b/src/test/java/de/neemann/digital/gui/components/table/LaTeXExpressionListenerTest.java index b7611eab9..478c2d76a 100644 --- a/src/test/java/de/neemann/digital/gui/components/table/LaTeXExpressionListenerTest.java +++ b/src/test/java/de/neemann/digital/gui/components/table/LaTeXExpressionListenerTest.java @@ -21,8 +21,7 @@ public class LaTeXExpressionListenerTest extends TestCase { .addVariable("A") .addVariable("B") .addResult("Y", new BoolTableBoolArray(new boolean[]{false, false, true, false})); - TruthTableTableModel model = new TruthTableTableModel(tt); - LaTeXExpressionListener l = new LaTeXExpressionListener(model); + LaTeXExpressionListener l = new LaTeXExpressionListener(tt); l.resultFound("Y", Operation.and(new Variable("A"), new Not(new Variable("B")))); l.close(); diff --git a/src/test/java/de/neemann/digital/gui/components/table/TestReorderInputs.java b/src/test/java/de/neemann/digital/gui/components/table/TestReorderInputs.java index 5c623756d..50c1f9f30 100644 --- a/src/test/java/de/neemann/digital/gui/components/table/TestReorderInputs.java +++ b/src/test/java/de/neemann/digital/gui/components/table/TestReorderInputs.java @@ -21,9 +21,10 @@ public class TestReorderInputs extends TestCase { col.set(i, i + 1); - ReorderInputs reorderInputs = new ReorderInputs(t); + TruthTable newTable = t.createDeepCopy(); + ReorderInputs reorderInputs = new ReorderInputs(newTable); reorderInputs.getItems().swap(1, 2); - TruthTable newTable = reorderInputs.reorder(); + reorderInputs.reorder(); ContextFiller cf = new ContextFiller(t.getVars()); @@ -39,9 +40,10 @@ public class TestReorderInputs extends TestCase { for (int i = 0; i < t.getRows(); i++) col.set(i, i + 1); - ReorderInputs reorderInputs = new ReorderInputs(t); + TruthTable newTable = t.createDeepCopy(); + ReorderInputs reorderInputs = new ReorderInputs(newTable); reorderInputs.getItems().delete(2); - TruthTable newTable = reorderInputs.reorder(); + reorderInputs.reorder(); assertEquals(2, newTable.getVars().size()); assertEquals(1, newTable.getResultCount()); diff --git a/src/test/java/de/neemann/digital/gui/components/table/TestReorderOutputs.java b/src/test/java/de/neemann/digital/gui/components/table/TestReorderOutputs.java index bbaac7059..56f3c2a14 100644 --- a/src/test/java/de/neemann/digital/gui/components/table/TestReorderOutputs.java +++ b/src/test/java/de/neemann/digital/gui/components/table/TestReorderOutputs.java @@ -19,9 +19,10 @@ public class TestReorderOutputs extends TestCase { for (int i = 0; i < t.getRows(); i++) col.set(i, i + 1); - ReorderOutputs reorderOutputs = new ReorderOutputs(t); + TruthTable newTable = t.createDeepCopy(); + ReorderOutputs reorderOutputs = new ReorderOutputs(newTable); reorderOutputs.getItems().delete(1); - TruthTable newTable = reorderOutputs.reorder(); + reorderOutputs.reorder(); assertEquals(3, newTable.getVars().size()); assertEquals(1, newTable.getResultCount());