/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.gui.menu;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.ButtonGroup;
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.gui.log.LogFrame;
import com.cburch.logisim.proj.Project;
import com.cburch.logisim.util.StringUtil;
import java.util.ArrayList;
class MenuSimulate extends Menu {
private class TickFrequencyChoice extends JRadioButtonMenuItem
implements ActionListener {
private double freq;
public TickFrequencyChoice(double value) {
freq = value;
addActionListener(this);
}
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));
}
}
}
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);
}
void unregister() {
Circuit circuit = circuitState.getCircuit();
circuit.removeCircuitListener(this);
}
public void circuitChanged(CircuitEvent event) {
if (event.getAction() == CircuitEvent.ACTION_SET_NAME) {
this.setText(circuitState.getCircuit().getName());
}
}
public void actionPerformed(ActionEvent e) {
menubar.fireStateChanged(currentSim, circuitState);
}
}
private class MyListener implements ActionListener, SimulatorListener,
ChangeListener {
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) 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 == 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);
}
}
public void propagationCompleted(SimulatorEvent e) { }
public void tickCompleted(SimulatorEvent e) { }
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);
}
}
public void stateChanged(ChangeEvent e) {
step.setEnabled(run.isEnabled() && !run.isSelected());
}
}
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 MenuItemCheckImpl ticksEnabled;
private MenuItemImpl tickOnce;
private JMenu tickFreq = new JMenu();
private TickFrequencyChoice[] tickFreqs = {
new TickFrequencyChoice(4096),
new TickFrequencyChoice(2048),
new TickFrequencyChoice(1024),
new TickFrequencyChoice(512),
new TickFrequencyChoice(256),
new TickFrequencyChoice(128),
new TickFrequencyChoice(64),
new TickFrequencyChoice(32),
new TickFrequencyChoice(16),
new TickFrequencyChoice(8),
new TickFrequencyChoice(4),
new TickFrequencyChoice(2),
new TickFrequencyChoice(1),
new TickFrequencyChoice(0.5),
new TickFrequencyChoice(0.25),
};
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();
public MenuSimulate(LogisimMenuBar menubar) {
this.menubar = menubar;
run = new MenuItemCheckImpl(this, LogisimMenuBar.SIMULATE_ENABLE);
step = new MenuItemImpl(this, LogisimMenuBar.SIMULATE_STEP);
ticksEnabled = new MenuItemCheckImpl(this, LogisimMenuBar.TICK_ENABLE);
tickOnce = new MenuItemImpl(this, LogisimMenuBar.TICK_STEP);
menubar.registerItem(LogisimMenuBar.SIMULATE_ENABLE, run);
menubar.registerItem(LogisimMenuBar.SIMULATE_STEP, step);
menubar.registerItem(LogisimMenuBar.TICK_ENABLE, ticksEnabled);
menubar.registerItem(LogisimMenuBar.TICK_STEP, tickOnce);
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));
ticksEnabled.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_K, menuMask));
ButtonGroup bgroup = new ButtonGroup();
for (int i = 0; i < tickFreqs.length; i++) {
bgroup.add(tickFreqs[i]);
tickFreq.add(tickFreqs[i]);
}
add(run);
add(reset);
add(step);
addSeparator();
add(upStateMenu);
add(downStateMenu);
addSeparator();
add(tickOnce);
add(ticksEnabled);
add(tickFreq);
addSeparator();
add(log);
setEnabled(false);
run.setEnabled(false);
reset.setEnabled(false);
step.setEnabled(false);
upStateMenu.setEnabled(false);
downStateMenu.setEnabled(false);
tickOnce.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.TICK_ENABLE, myListener);
menubar.addActionListener(LogisimMenuBar.TICK_STEP, myListener);
// run.addActionListener(myListener);
reset.addActionListener(myListener);
// step.addActionListener(myListener);
// tickOnce.addActionListener(myListener);
// ticksEnabled.addActionListener(myListener);
log.addActionListener(myListener);
computeEnabled();
}
public void localeChanged() {
this.setText(Strings.get("simulateMenu"));
run.setText(Strings.get("simulateRunItem"));
reset.setText(Strings.get("simulateResetItem"));
step.setText(Strings.get("simulateStepItem"));
tickOnce.setText(Strings.get("simulateTickOnceItem"));
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"));
}
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();
}
private void clearItems(ArrayList<CircuitStateMenuItem> items) {
for (CircuitStateMenuItem item : items) {
item.unregister();
}
items.clear();
}
private void recreateStateMenus() {
recreateStateMenu(downStateMenu, downStateItems, KeyEvent.VK_RIGHT);
recreateStateMenu(upStateMenu, upStateItems, KeyEvent.VK_LEFT);
}
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);
}
}
}
@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);
upStateMenu.setEnabled(present);
downStateMenu.setEnabled(present);
tickOnce.setEnabled(present);
ticksEnabled.setEnabled(present && simRunning);
tickFreq.setEnabled(present);
menubar.fireEnableChanged();
}
}