2018-11-25 12:22:30 +01:00

407 lines
15 KiB
Java

/*
* Copyright (c) 2018 Helmut Neemann.
* Use of this source code is governed by the GPL v3 license
* that can be found in the LICENSE file.
*/
package de.neemann.digital.fsm.gui;
import de.neemann.digital.core.element.ElementAttributes;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.fsm.FSM;
import de.neemann.digital.fsm.FSMDemos;
import de.neemann.digital.gui.SaveAsHelper;
import de.neemann.digital.gui.Settings;
import de.neemann.digital.gui.components.table.TableDialog;
import de.neemann.digital.lang.Lang;
import de.neemann.gui.*;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.prefs.Preferences;
/**
* The dialog to show the FSM
*/
public class FSMFrame extends JFrame implements ClosingWindowListener.ConfirmSave, FSM.ModifiedListener {
private static final Preferences PREFS = Preferences.userRoot().node("dig").node("fsm");
private static final String PREF_FOLDER = "folder";
private static final Icon ICON_NEW = IconCreator.create("document-new.png");
private static final Icon ICON_OPEN = IconCreator.create("document-open.png");
private static final Icon ICON_SAVE = IconCreator.create("document-save.png");
private static final Icon ICON_SAVE_AS = IconCreator.create("document-save-as.png");
private static final Icon ICON_EXPAND = IconCreator.create("View-zoom-fit.png");
private static final Icon ICON_ZOOM_IN = IconCreator.create("View-zoom-in.png");
private static final Icon ICON_ZOOM_OUT = IconCreator.create("View-zoom-out.png");
private FSM fsm;
private final FSMComponent fsmComponent;
private final Timer timer;
private final JComboBox<String> moveControl;
private boolean moveStates = false;
private ToolTipAction save;
private File filename;
private boolean lastModified;
/**
* Creates a new instance
*
* @param parent the parents frame
* @param givenFsm the fsm to visualize
* @param library the library used to show the table
*/
public FSMFrame(JFrame parent, FSM givenFsm, ElementLibrary library) {
super(Lang.get("fsm_title"));
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
if (givenFsm == null) {
givenFsm = FSMDemos.rotDecoder();
givenFsm.circle();
}
fsmComponent = new FSMComponent();
getContentPane().add(fsmComponent, BorderLayout.CENTER);
timer = new Timer(100, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
for (int i = 0; i < 100; i++)
fsm.move(10, moveStates, fsmComponent.getElementMoved());
repaint();
}
});
addWindowListener(new ClosingWindowListener(this, this));
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent windowEvent) {
timer.stop();
}
});
JMenuBar bar = new JMenuBar();
JToolBar toolBar = new JToolBar();
createFileMenu(bar, toolBar);
toolBar.addSeparator();
createViewMenu(bar, toolBar);
toolBar.addSeparator();
createCreateMenu(bar, library);
moveControl = new JComboBox<>(new String[]{
Lang.get("fsm_noMove"), Lang.get("fsm_moveTrans"), Lang.get("fsm_moveStates")});
moveControl.setSelectedIndex(0);
moveControl.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
switch (moveControl.getSelectedIndex()) {
case 0:
timer.stop();
fsm.toRaster();
fsmComponent.repaint();
break;
case 1:
if (moveStates)
fsm.toRaster();
moveStates = false;
timer.start();
break;
case 2:
moveStates = true;
timer.start();
break;
}
}
});
JPanel movePanel = new JPanel(new BorderLayout());
movePanel.add(moveControl, BorderLayout.WEST);
toolBar.add(movePanel);
getContentPane().add(toolBar, BorderLayout.PAGE_START);
setJMenuBar(bar);
pack();
setFSM(givenFsm);
setLocationRelativeTo(parent);
}
private void createFileMenu(JMenuBar bar, JToolBar toolBar) {
ToolTipAction newFile = new ToolTipAction(Lang.get("menu_new"), ICON_NEW) {
@Override
public void actionPerformed(ActionEvent e) {
if (ClosingWindowListener.checkForSave(FSMFrame.this, FSMFrame.this)) {
setFSM(new FSM());
setFilename(null);
}
}
}.setAcceleratorCTRLplus('N').setToolTip(Lang.get("menu_new_tt"));
ToolTipAction open = new ToolTipAction(Lang.get("menu_open"), ICON_OPEN) {
@Override
public void actionPerformed(ActionEvent e) {
if (ClosingWindowListener.checkForSave(FSMFrame.this, FSMFrame.this)) {
JFileChooser fc = getJFileChooser(filename);
if (fc.showOpenDialog(FSMFrame.this) == JFileChooser.APPROVE_OPTION) {
loadFile(fc.getSelectedFile());
}
}
}
}.setAcceleratorCTRLplus('O');
// JMenu openRecent = new JMenu(Lang.get("menu_openRecent"));
// JMenu openRecentNewWindow = new JMenu(Lang.get("menu_openRecentNewWindow"));
// fileHistory.setMenu(openRecent, openRecentNewWindow);
ToolTipAction saveAs = new ToolTipAction(Lang.get("menu_saveAs"), ICON_SAVE_AS) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = getJFileChooser(filename);
final SaveAsHelper saveAsHelper = new SaveAsHelper(FSMFrame.this, fc, "fsm");
saveAsHelper.checkOverwrite(file -> saveFile(file));
}
};
save = new ToolTipAction(Lang.get("menu_save"), ICON_SAVE) {
@Override
public void actionPerformed(ActionEvent e) {
if (filename == null)
saveAs.actionPerformed(e);
else
saveFile(filename);
}
}.setAcceleratorCTRLplus('S').setEnabledChain(false);
JMenu export = new JMenu(Lang.get("menu_export"));
export.add(new ExportAction(Lang.get("menu_exportSVG"), "svg", GraphicSVGIndex::new));
export.add(new ExportAction(Lang.get("menu_exportSVGLaTex"), "svg", GraphicSVGLaTeX::new));
JMenu file = new JMenu(Lang.get("menu_file"));
bar.add(file);
file.add(newFile.createJMenuItem());
file.add(open.createJMenuItem());
file.add(save.createJMenuItem());
file.add(saveAs.createJMenuItem());
file.add(export);
toolBar.add(newFile.createJButtonNoText());
toolBar.add(open.createJButtonNoText());
toolBar.add(save.createJButtonNoText());
}
private void setFSM(FSM fsm) {
this.fsm = fsm;
fsmComponent.setFSM(fsm);
fsm.setModifiedListener(this);
}
private static JFileChooser getJFileChooser(File filename) {
File folder = null;
if (filename != null)
folder = filename.getParentFile();
if (folder == null) {
String folderStr = PREFS.get(PREF_FOLDER, null);
if (folderStr != null)
folder = new File(folderStr);
}
JFileChooser fileChooser = new MyFileChooser(folder);
fileChooser.setFileFilter(new FileNameExtensionFilter("FSM", "fsm"));
return fileChooser;
}
private void setFilename(File filename) {
String fsmTitle;
if (filename == null)
fsmTitle = Lang.get("fsm_title");
else
fsmTitle = filename.toString() + " - " + Lang.get("fsm_title");
if (fsm.isModified())
fsmTitle = "*" + fsmTitle;
setTitle(fsmTitle);
this.filename = filename;
}
@Override
public boolean isStateChanged() {
return fsm.isModified();
}
@Override
public void saveChanges() {
save.actionPerformed(null);
}
@Override
public void modifiedChanged(boolean modified) {
if (lastModified != modified) {
lastModified = modified;
setFilename(filename);
save.setEnabled(modified);
}
}
private void loadFile(File file) {
try {
moveControl.setSelectedIndex(0);
setFSM(FSM.loadFSM(file));
setFilename(file);
lastModified = fsm.isModified();
PREFS.put(PREF_FOLDER, file.getParent());
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_fsm_errorLoadingFile")).addCause(e).show(this);
}
}
private void saveFile(File file) {
try {
moveControl.setSelectedIndex(0);
fsm.save(file);
setFilename(file);
save.setEnabled(false);
lastModified = fsm.isModified();
PREFS.put(PREF_FOLDER, file.getParent());
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_fsm_errorStoringFile")).addCause(e).show(this);
}
}
private void createViewMenu(JMenuBar menuBar, JToolBar toolBar) {
ToolTipAction maximize = new ToolTipAction(Lang.get("menu_maximize"), ICON_EXPAND) {
@Override
public void actionPerformed(ActionEvent e) {
fsmComponent.fitFSM();
}
}.setAccelerator("F1");
ToolTipAction zoomIn = new ToolTipAction(Lang.get("menu_zoomIn"), ICON_ZOOM_IN) {
@Override
public void actionPerformed(ActionEvent e) {
fsmComponent.scaleCircuit(1 / 0.9);
}
}.setAccelerator("control PLUS");
// enable [+] which is SHIFT+[=] on english keyboard layout
fsmComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, KeyEvent.CTRL_DOWN_MASK, false), zoomIn);
fsmComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, KeyEvent.CTRL_DOWN_MASK, false), zoomIn);
fsmComponent.getActionMap().put(zoomIn, zoomIn);
ToolTipAction zoomOut = new ToolTipAction(Lang.get("menu_zoomOut"), ICON_ZOOM_OUT) {
@Override
public void actionPerformed(ActionEvent e) {
fsmComponent.scaleCircuit(0.9);
}
}.setAccelerator("control MINUS");
// enable [+] which is SHIFT+[=] on english keyboard layout
fsmComponent.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, KeyEvent.CTRL_DOWN_MASK, false), zoomOut);
fsmComponent.getActionMap().put(zoomOut, zoomOut);
toolBar.add(zoomIn.createJButtonNoText());
toolBar.add(zoomOut.createJButtonNoText());
toolBar.add(maximize.createJButtonNoText());
JMenu view = new JMenu(Lang.get("menu_view"));
menuBar.add(view);
view.add(maximize.createJMenuItem());
view.add(zoomOut.createJMenuItem());
view.add(zoomIn.createJMenuItem());
}
private void createCreateMenu(JMenuBar bar, ElementLibrary library) {
JMenu create = new JMenu(Lang.get("menu_fsm_create"));
bar.add(create);
create.add(new ToolTipAction(Lang.get("menu_fsm_create_table")) {
@Override
public void actionPerformed(ActionEvent actionEvent) {
try {
new TableDialog(FSMFrame.this, fsm.createTruthTable(), library, null).setVisible(true);
} catch (Exception e) {
new ErrorMessage(Lang.get("msg_fsmCantCreateTable")).addCause(e).show(FSMFrame.this);
}
}
}.createJMenuItem());
JMenu counter = new JMenu(Lang.get("menu_fsm_create_counter"));
create.add(counter);
int[] counterValues = new int[]{4, 5, 6, 7, 8, 10, 16};
for (int n : counterValues) {
counter.add(new ToolTipAction(Lang.get("menu_fsm_create_counter_N", n)) {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (ClosingWindowListener.checkForSave(FSMFrame.this, FSMFrame.this)) {
setFSM(FSMDemos.counter(n).circle().setModified(false));
setFilename(null);
}
}
});
}
}
private class ExportAction extends ToolTipAction {
private final String name;
private final String suffix;
private final ExportFactory exportFactory;
ExportAction(String name, String suffix, ExportFactory exportFactory) {
super(name);
this.name = name;
this.suffix = suffix;
this.exportFactory = exportFactory;
}
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new MyFileChooser();
if (filename != null)
fc.setSelectedFile(SaveAsHelper.checkSuffix(filename, suffix));
ElementAttributes settings = Settings.getInstance().getAttributes();
File exportDir = settings.getFile("exportDirectory");
if (exportDir != null)
fc.setCurrentDirectory(exportDir);
fc.addChoosableFileFilter(new FileNameExtensionFilter(name, suffix));
new SaveAsHelper(FSMFrame.this, fc, suffix).checkOverwrite(
file -> {
settings.setFile("exportDirectory", file.getParentFile());
try (OutputStream out = new FileOutputStream(file)) {
try (Graphic gr = exportFactory.create(out)) {
GraphicMinMax minMax = new GraphicMinMax(gr);
fsm.drawTo(minMax);
gr.setBoundingBox(minMax.getMin(), minMax.getMax());
fsm.drawTo(gr);
}
}
}
);
}
}
/**
* A simple test method
*
* @param args the programs arguments
*/
public static void main(String[] args) {
FSM fsm = FSMDemos.rotDecoder();
ElementLibrary library = new ElementLibrary();
new ShapeFactory(library);
new FSMFrame(null, fsm.circle().setModified(false), library).setVisible(true);
}
}