/******************************************************************************* * This file is part of logisim-evolution. * * logisim-evolution is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * logisim-evolution is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>. * * Original code by Carl Burch (http://www.cburch.com), 2011. * Subsequent modifications by : * + Haute École Spécialisée Bernoise * http://www.bfh.ch * + Haute École du paysage, d'ingénierie et d'architecture de Genève * http://hepia.hesge.ch/ * + Haute École d'Ingénierie et de Gestion du Canton de Vaud * http://www.heig-vd.ch/ * The project is currently maintained by : * + REDS Institute - HEIG-VD * Yverdon-les-Bains, Switzerland * http://reds.heig-vd.ch *******************************************************************************/ package com.cburch.logisim.analyze.gui; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.KeyEvent; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.KeyStroke; import javax.swing.ScrollPaneConstants; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import com.cburch.logisim.analyze.model.AnalyzerModel; import com.cburch.logisim.analyze.model.Expression; import com.cburch.logisim.analyze.model.OutputExpressionsEvent; import com.cburch.logisim.analyze.model.OutputExpressionsListener; import com.cburch.logisim.analyze.model.Parser; import com.cburch.logisim.analyze.model.ParserException; import com.cburch.logisim.util.StringGetter; class ExpressionTab extends AnalyzerTab implements TabInterface { private class MyListener extends AbstractAction implements DocumentListener, OutputExpressionsListener, ItemListener { private static final long serialVersionUID = 1L; boolean edited = false; public void actionPerformed(ActionEvent event) { Object src = event.getSource(); if (src == clear) { setError(null); field.setText(""); field.grabFocus(); } else if (src == revert) { setError(null); field.setText(getCurrentString()); field.grabFocus(); } else if ((src == field || src == enter) && enter.isEnabled()) { try { String exprString = field.getText(); Expression expr = Parser.parse(field.getText(), model); setError(null); model.getOutputExpressions().setExpression( getCurrentVariable(), expr, exprString); insertUpdate(null); } catch (ParserException ex) { setError(ex.getMessageGetter()); field.setCaretPosition(ex.getOffset()); field.moveCaretPosition(ex.getEndOffset()); } field.grabFocus(); } } public void changedUpdate(DocumentEvent event) { insertUpdate(event); } private void currentStringChanged() { String output = getCurrentVariable(); String exprString = model.getOutputExpressions() .getExpressionString(output); curExprStringLength = exprString.length(); if (!edited) { setError(null); field.setText(getCurrentString()); } else { insertUpdate(null); } } public void expressionChanged(OutputExpressionsEvent event) { if (event.getType() == OutputExpressionsEvent.OUTPUT_EXPRESSION) { String output = event.getVariable(); if (output.equals(getCurrentVariable())) { prettyView.setExpression(model.getOutputExpressions() .getExpression(output)); currentStringChanged(); } } } private String getCurrentString() { String output = getCurrentVariable(); return output == null ? "" : model.getOutputExpressions() .getExpressionString(output); } public void insertUpdate(DocumentEvent event) { String curText = field.getText(); edited = curText.length() != curExprStringLength || !curText.equals(getCurrentString()); boolean enable = (edited && getCurrentVariable() != null); clear.setEnabled(curText.length() > 0); revert.setEnabled(enable); enter.setEnabled(enable); } public void itemStateChanged(ItemEvent event) { updateTab(); } public void removeUpdate(DocumentEvent event) { insertUpdate(event); } } private static final long serialVersionUID = 1L; private OutputSelector selector; private ExpressionView prettyView = new ExpressionView(); private JTextArea field = new JTextArea(4, 25); private JButton clear = new JButton(); private JButton revert = new JButton(); private JButton enter = new JButton(); private JLabel error = new JLabel(); private MyListener myListener = new MyListener(); private AnalyzerModel model; private int curExprStringLength = 0; private StringGetter errorMessage; public ExpressionTab(AnalyzerModel model) { this.model = model; selector = new OutputSelector(model); model.getOutputExpressions().addOutputExpressionsListener(myListener); selector.addItemListener(myListener); clear.addActionListener(myListener); revert.addActionListener(myListener); enter.addActionListener(myListener); field.setLineWrap(true); field.setWrapStyleWord(true); field.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), myListener); field.getDocument().addDocumentListener(myListener); field.setFont(new Font("Monospaced", Font.PLAIN, 14)); JPanel buttons = new JPanel(); buttons.add(clear); buttons.add(revert); buttons.add(enter); GridBagLayout gb = new GridBagLayout(); GridBagConstraints gc = new GridBagConstraints(); setLayout(gb); gc.weightx = 1.0; gc.gridx = 0; gc.gridy = GridBagConstraints.RELATIVE; gc.fill = GridBagConstraints.BOTH; JPanel selectorPanel = selector.createPanel(); gb.setConstraints(selectorPanel, gc); add(selectorPanel); gb.setConstraints(prettyView, gc); add(prettyView); Insets oldInsets = gc.insets; gc.insets = new Insets(10, 10, 0, 10); JScrollPane fieldPane = new JScrollPane(field, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); gb.setConstraints(fieldPane, gc); add(fieldPane); gc.insets = oldInsets; gc.fill = GridBagConstraints.NONE; gc.anchor = GridBagConstraints.LINE_END; gb.setConstraints(buttons, gc); add(buttons); gc.fill = GridBagConstraints.BOTH; gb.setConstraints(error, gc); add(error); myListener.insertUpdate(null); setError(null); } public void copy() { field.requestFocus(); field.copy(); } public void delete() { field.requestFocus(); field.replaceSelection(""); } String getCurrentVariable() { return selector.getSelectedOutput(); } @Override void localeChanged() { selector.localeChanged(); prettyView.localeChanged(); clear.setText(Strings.get("exprClearButton")); revert.setText(Strings.get("exprRevertButton")); enter.setText(Strings.get("exprEnterButton")); if (errorMessage != null) { error.setText(errorMessage.toString()); } } public void paste() { field.requestFocus(); field.paste(); } void registerDefaultButtons(DefaultRegistry registry) { registry.registerDefaultButton(field, enter); } public void selectAll() { field.requestFocus(); field.selectAll(); } private void setError(StringGetter msg) { if (msg == null) { errorMessage = null; error.setText(" "); } else { errorMessage = msg; error.setText(msg.toString()); } } @Override void updateTab() { String output = getCurrentVariable(); prettyView.setExpression(model.getOutputExpressions().getExpression( output)); myListener.currentStringChanged(); } }