/******************************************************************************* * 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.gui.menu; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.util.ArrayList; import javax.swing.ButtonGroup; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JRadioButtonMenuItem; import javax.swing.KeyStroke; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.cburch.logisim.circuit.Circuit; import com.cburch.logisim.circuit.CircuitEvent; import com.cburch.logisim.circuit.CircuitListener; import com.cburch.logisim.circuit.CircuitState; import com.cburch.logisim.circuit.Simulator; import com.cburch.logisim.circuit.SimulatorEvent; import com.cburch.logisim.circuit.SimulatorListener; import com.cburch.logisim.file.Options; import com.cburch.logisim.gui.log.LogFrame; import com.cburch.logisim.gui.test.TestFrame; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.proj.Project; import com.cburch.logisim.std.wiring.Clock; import com.cburch.logisim.util.StringUtil; @SuppressWarnings("serial") public class MenuSimulate extends Menu { private class CircuitStateMenuItem extends JMenuItem implements CircuitListener, ActionListener { private CircuitState circuitState; public CircuitStateMenuItem(CircuitState circuitState) { this.circuitState = circuitState; Circuit circuit = circuitState.getCircuit(); circuit.addCircuitListener(this); this.setText(circuit.getName()); addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { menubar.fireStateChanged(currentSim, circuitState); } @Override public void circuitChanged(CircuitEvent event) { if (event.getAction() == CircuitEvent.ACTION_SET_NAME) { this.setText(circuitState.getCircuit().getName()); } } void unregister() { Circuit circuit = circuitState.getCircuit(); circuit.removeCircuitListener(this); } } private class MyListener implements ActionListener, SimulatorListener, ChangeListener { @Override public void actionPerformed(ActionEvent e) { Object src = e.getSource(); Project proj = menubar.getProject(); Simulator sim = proj == null ? null : proj.getSimulator(); if (src == run || src == LogisimMenuBar.SIMULATE_ENABLE) { if (sim != null) { sim.setIsRunning(!sim.isRunning()); proj.repaintCanvas(); } } else if (src == reset) { if (sim != null) { /* Restart VHDL simulation (in QuestaSim) */ if (sim.getCircuitState().getProject().getVhdlSimulator() .isRunning()) { sim.getCircuitState().getProject().getVhdlSimulator() .reset(); /* * We have to wait until the restart finishes, otherwise * the signal reset will be sent to the VHDL simulator * before the sim is loaded and errors will occur Time * (0,5s) is arbitrary * * FIXME: if you find a way to make a blocking reset * until it's restarted, feel free to go on */ try { Thread.sleep(500); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } sim.requestReset(); } } else if (src == step || src == LogisimMenuBar.SIMULATE_STEP) { if (sim != null) { sim.step(); } } else if (src == tickOnce || src == LogisimMenuBar.TICK_STEP) { if (sim != null) { sim.tick(); } } else if (src == simulate_vhdl_enable || src == LogisimMenuBar.SIMULATE_VHDL_ENABLE) { if (proj.getVhdlSimulator() != null) { proj.getVhdlSimulator().setEnabled( !proj.getVhdlSimulator().isEnabled()); } } else if (src == vhdl_sim_files || src == LogisimMenuBar.GENERATE_VHDL_SIM_FILES) { if (proj.getVhdlSimulator() != null) { proj.getVhdlSimulator().restart(); } } else if (src == tickOnce || src == LogisimMenuBar.TICK_STEP_MAIN) { int ticks = 0; for (com.cburch.logisim.comp.Component clock : proj .getLogisimFile().getMainCircuit().getClocks()) { if (clock.getAttributeSet().getValue(StdAttr.LABEL) .contentEquals("clk")) { if (proj.getOptions().getAttributeSet() .getValue(Options.ATTR_TICK_MAIN) .equals(Options.TICK_MAIN_HALF_PERIOD)) { if (currentState.getValue(clock.getLocation()) .toIntValue() == 0) { ticks = clock.getAttributeSet().getValue( Clock.ATTR_LOW); } else { ticks = clock.getAttributeSet().getValue( Clock.ATTR_HIGH); } } else { ticks = clock.getAttributeSet().getValue( Clock.ATTR_LOW) + clock.getAttributeSet().getValue( Clock.ATTR_HIGH); } break; } } sim.tickMain(ticks); } else if (src == ticksEnabled || src == LogisimMenuBar.TICK_ENABLE) { if (sim != null) { sim.setIsTicking(!sim.isTicking()); } } else if (src == log) { LogFrame frame = menubar.getProject().getLogFrame(true); frame.setVisible(true); } else if (src == assemblyWindow) { if (assWin == null || assWin.isVisible() == false) { assWin = new AssemblyWindow(proj); assWin.setVisible(true); } else { assWin.toFront(); } } else if (src == test) { TestFrame frame = menubar.getProject().getTestFrame(true); frame.setVisible(true); } } @Override public void propagationCompleted(SimulatorEvent e) { } @Override public void simulatorStateChanged(SimulatorEvent e) { Simulator sim = e.getSource(); if (sim != currentSim) { return; } computeEnabled(); run.setSelected(sim.isRunning()); ticksEnabled.setSelected(sim.isTicking()); double freq = sim.getTickFrequency(); for (int i = 0; i < tickFreqs.length; i++) { TickFrequencyChoice item = tickFreqs[i]; item.setSelected(freq == item.freq); } } @Override public void stateChanged(ChangeEvent e) { step.setEnabled(run.isEnabled() && !run.isSelected()); } @Override public void tickCompleted(SimulatorEvent e) { } } private class TickFrequencyChoice extends JRadioButtonMenuItem implements ActionListener { private double freq; public TickFrequencyChoice(double value) { freq = value; addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if (currentSim != null) { currentSim.setTickFrequency(freq); } } public void localeChanged() { double f = freq; if (f < 1000) { String hzStr; if (Math.abs(f - Math.round(f)) < 0.0001) { hzStr = "" + (int) Math.round(f); } else { hzStr = "" + f; } setText(StringUtil.format(Strings.get("simulateTickFreqItem"), hzStr)); } else { String kHzStr; double kf = Math.round(f / 100) / 10.0; if (kf == Math.round(kf)) { kHzStr = "" + (int) kf; } else { kHzStr = "" + kf; } setText(StringUtil.format(Strings.get("simulateTickKFreqItem"), kHzStr)); } } } public static ArrayList<String> getTickFrequencyStrings() { ArrayList<String> result = new ArrayList<String>(); for (int i = 0; i < SupportedTickFrequencies.length; i++) { if (SupportedTickFrequencies[i] < 1000) { String hzStr; if (Math.abs(SupportedTickFrequencies[i] - Math.round(SupportedTickFrequencies[i])) < 0.0001) { hzStr = "" + (int) Math.round(SupportedTickFrequencies[i]); } else { hzStr = "" + SupportedTickFrequencies[i]; } result.add(StringUtil.format( Strings.get("simulateTickFreqItem"), hzStr)); } else { String kHzStr; double kf = Math.round(SupportedTickFrequencies[i] / 100) / 10.0; if (kf == Math.round(kf)) { kHzStr = "" + (int) kf; } else { kHzStr = "" + kf; } result.add(StringUtil.format( Strings.get("simulateTickKFreqItem"), kHzStr)); } } return result; } public static final Double[] SupportedTickFrequencies = { 4096.0, 2048.0, 1024.0, 512.0, 256.0, 128.0, 64.0, 32.0, 16.0, 8.0, 4.0, 2.0, 1.0, 0.5, 0.25 }; private LogisimMenuBar menubar; private MyListener myListener = new MyListener(); private CircuitState currentState = null; private CircuitState bottomState = null; private Simulator currentSim = null; private MenuItemCheckImpl run; private JMenuItem reset = new JMenuItem(); private MenuItemImpl step; private MenuItemImpl vhdl_sim_files; private MenuItemCheckImpl simulate_vhdl_enable; private MenuItemCheckImpl ticksEnabled; private MenuItemImpl tickOnce; private MenuItemImpl tickOnceMain; private JMenu tickFreq = new JMenu(); private TickFrequencyChoice[] tickFreqs = new TickFrequencyChoice[SupportedTickFrequencies.length]; private JMenu downStateMenu = new JMenu(); private ArrayList<CircuitStateMenuItem> downStateItems = new ArrayList<CircuitStateMenuItem>(); private JMenu upStateMenu = new JMenu(); private ArrayList<CircuitStateMenuItem> upStateItems = new ArrayList<CircuitStateMenuItem>(); private JMenuItem log = new JMenuItem(); private JMenuItem test = new JMenuItem(); private JMenuItem assemblyWindow = new JMenuItem(); AssemblyWindow assWin = null; public MenuSimulate(LogisimMenuBar menubar) { this.menubar = menubar; run = new MenuItemCheckImpl(this, LogisimMenuBar.SIMULATE_ENABLE); step = new MenuItemImpl(this, LogisimMenuBar.SIMULATE_STEP); simulate_vhdl_enable = new MenuItemCheckImpl(this, LogisimMenuBar.SIMULATE_VHDL_ENABLE); vhdl_sim_files = new MenuItemImpl(this, LogisimMenuBar.GENERATE_VHDL_SIM_FILES); ticksEnabled = new MenuItemCheckImpl(this, LogisimMenuBar.TICK_ENABLE); tickOnce = new MenuItemImpl(this, LogisimMenuBar.TICK_STEP); tickOnceMain = new MenuItemImpl(this, LogisimMenuBar.TICK_STEP_MAIN); menubar.registerItem(LogisimMenuBar.SIMULATE_ENABLE, run); menubar.registerItem(LogisimMenuBar.SIMULATE_STEP, step); menubar.registerItem(LogisimMenuBar.SIMULATE_VHDL_ENABLE, simulate_vhdl_enable); menubar.registerItem(LogisimMenuBar.GENERATE_VHDL_SIM_FILES, vhdl_sim_files); menubar.registerItem(LogisimMenuBar.TICK_ENABLE, ticksEnabled); menubar.registerItem(LogisimMenuBar.TICK_STEP, tickOnce); menubar.registerItem(LogisimMenuBar.TICK_STEP_MAIN, tickOnceMain); int menuMask = getToolkit().getMenuShortcutKeyMask(); run.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, menuMask)); reset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, menuMask)); step.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I, menuMask)); tickOnce.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, menuMask)); tickOnceMain.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)); ticksEnabled.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_K, menuMask)); ButtonGroup bgroup = new ButtonGroup(); for (int i = 0; i < SupportedTickFrequencies.length; i++) { tickFreqs[i] = new TickFrequencyChoice(SupportedTickFrequencies[i]); bgroup.add(tickFreqs[i]); tickFreq.add(tickFreqs[i]); } add(run); add(reset); add(step); add(simulate_vhdl_enable); add(vhdl_sim_files); addSeparator(); add(upStateMenu); add(downStateMenu); addSeparator(); add(tickOnce); add(tickOnceMain); add(ticksEnabled); add(tickFreq); addSeparator(); add(log); add(test); addSeparator(); add(assemblyWindow); setEnabled(false); run.setEnabled(false); reset.setEnabled(false); step.setEnabled(false); simulate_vhdl_enable.setEnabled(false); vhdl_sim_files.setEnabled(false); upStateMenu.setEnabled(false); downStateMenu.setEnabled(false); tickOnce.setEnabled(false); tickOnceMain.setEnabled(false); ticksEnabled.setEnabled(false); tickFreq.setEnabled(false); run.addChangeListener(myListener); menubar.addActionListener(LogisimMenuBar.SIMULATE_ENABLE, myListener); menubar.addActionListener(LogisimMenuBar.SIMULATE_STEP, myListener); menubar.addActionListener(LogisimMenuBar.SIMULATE_VHDL_ENABLE, myListener); menubar.addActionListener(LogisimMenuBar.GENERATE_VHDL_SIM_FILES, myListener); menubar.addActionListener(LogisimMenuBar.TICK_ENABLE, myListener); menubar.addActionListener(LogisimMenuBar.TICK_STEP, myListener); menubar.addActionListener(LogisimMenuBar.TICK_STEP_MAIN, myListener); // run.addActionListener(myListener); reset.addActionListener(myListener); // step.addActionListener(myListener); // tickOnce.addActionListener(myListener); // ticksEnabled.addActionListener(myListener); log.addActionListener(myListener); test.addActionListener(myListener); assemblyWindow.addActionListener(myListener); computeEnabled(); } private void clearItems(ArrayList<CircuitStateMenuItem> items) { for (CircuitStateMenuItem item : items) { item.unregister(); } items.clear(); } @Override void computeEnabled() { boolean present = currentState != null; Simulator sim = this.currentSim; boolean simRunning = sim != null && sim.isRunning(); setEnabled(present); run.setEnabled(present); reset.setEnabled(present); step.setEnabled(present && !simRunning); simulate_vhdl_enable.setEnabled(present); vhdl_sim_files.setEnabled(present); upStateMenu.setEnabled(present); downStateMenu.setEnabled(present); tickOnce.setEnabled(present); tickOnceMain.setEnabled(present); ticksEnabled.setEnabled(present && simRunning); tickFreq.setEnabled(present); menubar.fireEnableChanged(); } public void localeChanged() { this.setText(Strings.get("simulateMenu")); run.setText(Strings.get("simulateRunItem")); reset.setText(Strings.get("simulateResetItem")); step.setText(Strings.get("simulateStepItem")); simulate_vhdl_enable.setText(Strings.get("simulateVhdlEnableItem")); vhdl_sim_files.setText(Strings.get("simulateGenVhdlFilesItem")); tickOnce.setText(Strings.get("simulateTickOnceItem")); tickOnceMain.setText(Strings.get("simulateTickOnceMainItem")); ticksEnabled.setText(Strings.get("simulateTickItem")); tickFreq.setText(Strings.get("simulateTickFreqMenu")); for (int i = 0; i < tickFreqs.length; i++) { tickFreqs[i].localeChanged(); } downStateMenu.setText(Strings.get("simulateDownStateMenu")); upStateMenu.setText(Strings.get("simulateUpStateMenu")); log.setText(Strings.get("simulateLogItem")); test.setText(Strings.get("simulateTestItem")); assemblyWindow.setText("Assembly viewer"); } private void recreateStateMenu(JMenu menu, ArrayList<CircuitStateMenuItem> items, int code) { menu.removeAll(); menu.setEnabled(items.size() > 0); boolean first = true; int mask = getToolkit().getMenuShortcutKeyMask(); for (int i = items.size() - 1; i >= 0; i--) { JMenuItem item = items.get(i); menu.add(item); if (first) { item.setAccelerator(KeyStroke.getKeyStroke(code, mask)); first = false; } else { item.setAccelerator(null); } } } private void recreateStateMenus() { recreateStateMenu(downStateMenu, downStateItems, KeyEvent.VK_RIGHT); recreateStateMenu(upStateMenu, upStateItems, KeyEvent.VK_LEFT); } public void setCurrentState(Simulator sim, CircuitState value) { if (currentState == value) { return; } Simulator oldSim = currentSim; CircuitState oldState = currentState; currentSim = sim; currentState = value; if (bottomState == null) { bottomState = currentState; } else if (currentState == null) { bottomState = null; } else { CircuitState cur = bottomState; while (cur != null && cur != currentState) { cur = cur.getParentState(); } if (cur == null) { bottomState = currentState; } } boolean oldPresent = oldState != null; boolean present = currentState != null; if (oldPresent != present) { computeEnabled(); } if (currentSim != oldSim) { double freq = currentSim == null ? 1.0 : currentSim .getTickFrequency(); for (int i = 0; i < tickFreqs.length; i++) { tickFreqs[i] .setSelected(Math.abs(tickFreqs[i].freq - freq) < 0.001); } if (oldSim != null) { oldSim.removeSimulatorListener(myListener); } if (currentSim != null) { currentSim.addSimulatorListener(myListener); } myListener.simulatorStateChanged(new SimulatorEvent(sim)); } clearItems(downStateItems); CircuitState cur = bottomState; while (cur != null && cur != currentState) { downStateItems.add(new CircuitStateMenuItem(cur)); cur = cur.getParentState(); } if (cur != null) { cur = cur.getParentState(); } clearItems(upStateItems); while (cur != null) { upStateItems.add(0, new CircuitStateMenuItem(cur)); cur = cur.getParentState(); } recreateStateMenus(); } }