Simplification of event handling.

This commit is contained in:
hneemann 2017-03-28 21:09:05 +02:00
parent ead4194b91
commit b310a9a2e3
7 changed files with 110 additions and 132 deletions

View File

@ -1,20 +0,0 @@
package de.neemann.digital.draw.library;
import de.neemann.digital.core.element.ElementTypeDescription;
import java.io.IOException;
/**
* Used for lazy loading of the circuits
* Created by hneemann on 25.03.17.
*/
public interface DescriptionCreator {
/**
* Is called if the description is needed in the circuit.
* Is not called to create the menus
*
* @return the description
* @throws IOException IOException
*/
ElementTypeDescription createDescription() throws IOException;
}

View File

@ -302,7 +302,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
for (File f : orderedList) {
final String name = f.getName();
if (f.isFile() && name.endsWith(".dig"))
node.add(importElement(f));
node.add(new LibraryNode(f));
}
}
}
@ -362,8 +362,10 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
*
* @param name the elements name
*/
public void removeElement(File name) {
map.remove(name.getName());
public void invalidateElement(File name) {
LibraryNode n = map.get(name.getName());
if (n != null)
n.invalidate();
}
/**
@ -382,31 +384,36 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
return root;
}
private LibraryNode importElement(File file) {
return new LibraryNode(file.getName(), () -> {
/**
* Imports the given file
*
* @param file the file to load
* @return the description
* @throws IOException IOException
*/
ElementTypeDescription importElement(File file) throws IOException {
try {
LOGGER.debug("load element " + file);
Circuit circuit;
try {
LOGGER.debug("load element " + file);
Circuit circuit;
try {
circuit = Circuit.loadCircuit(file, shapeFactory);
} catch (IOException e) {
throw new IOException(Lang.get("err_couldNotFindIncludedFile_N0", file));
}
ElementTypeDescriptionCustom description =
new ElementTypeDescriptionCustom(file,
attributes -> new CustomElement(circuit, ElementLibrary.this, file),
circuit.getAttributes(), circuit.getInputNames());
description.setShortName(createShortName(file));
String descriptionText = circuit.getAttributes().get(Keys.DESCRIPTION);
if (descriptionText != null && descriptionText.length() > 0) {
description.setDescription(descriptionText);
}
return description;
} catch (PinException e) {
throw new IOException(Lang.get("msg_errorImportingModel"), e);
circuit = Circuit.loadCircuit(file, shapeFactory);
} catch (IOException e) {
throw new IOException(Lang.get("err_couldNotFindIncludedFile_N0", file));
}
});
ElementTypeDescriptionCustom description =
new ElementTypeDescriptionCustom(file,
attributes -> new CustomElement(circuit, ElementLibrary.this, file),
circuit.getAttributes(), circuit.getInputNames());
description.setShortName(createShortName(file));
String descriptionText = circuit.getAttributes().get(Keys.DESCRIPTION);
if (descriptionText != null && descriptionText.length() > 0) {
description.setDescription(descriptionText);
}
return description;
} catch (PinException e) {
throw new IOException(Lang.get("msg_errorImportingModel"), e);
}
}
private String createShortName(File file) {

View File

@ -5,6 +5,7 @@ import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.shapes.ShapeFactory;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
@ -18,7 +19,7 @@ public class LibraryNode implements Iterable<LibraryNode> {
private final ArrayList<LibraryNode> children;
private final String translatedName;
private final String name;
private final DescriptionCreator creator;
private final File file;
private ElementTypeDescription description;
private ImageIcon icon;
private ElementLibrary library;
@ -35,7 +36,7 @@ public class LibraryNode implements Iterable<LibraryNode> {
this.translatedName = name;
this.children = new ArrayList<>();
this.description = null;
this.creator = null;
this.file = null;
}
/**
@ -48,20 +49,19 @@ public class LibraryNode implements Iterable<LibraryNode> {
this.description = description;
this.name = description.getName();
this.translatedName = description.getTranslatedName();
this.creator = null;
this.file = null;
}
/**
* Creates a new leaf
*
* @param name the name of the leaf
* @param creator used to create the {@link ElementTypeDescription} if necessary
* @param file the file containing the leaf
*/
LibraryNode(String name, DescriptionCreator creator) {
LibraryNode(File file) {
this.children = null;
this.name = name;
this.name = file.getName();
this.translatedName = name;
this.creator = creator;
this.file = file;
}
/**
@ -100,7 +100,7 @@ public class LibraryNode implements Iterable<LibraryNode> {
* @return trie if this is a leaf
*/
public boolean isLeaf() {
return description != null || creator != null;
return description != null || file != null;
}
/**
@ -118,7 +118,7 @@ public class LibraryNode implements Iterable<LibraryNode> {
*/
public ElementTypeDescription getDescription() throws IOException {
if (description == null) {
description = creator.createDescription();
description = library.importElement(file);
library.fireLibraryChanged(this);
}
return description;
@ -163,8 +163,8 @@ public class LibraryNode implements Iterable<LibraryNode> {
/**
* @return returns the description if present, null otherwise
*/
public ElementTypeDescription getDescriptionOrNull() {
return description;
public boolean isCustom() {
return file != null;
}
/**
@ -263,4 +263,12 @@ public class LibraryNode implements Iterable<LibraryNode> {
return path.toArray(new Object[path.size()]);
}
/**
* Invalidate this node
*/
public void invalidate() {
description = null;
icon = null;
library.fireLibraryChanged(this);
}
}

View File

@ -2,7 +2,6 @@ package de.neemann.digital.gui;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.LibraryNode;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.CircuitComponent;
@ -59,7 +58,7 @@ public final class InsertAction extends ToolTipAction {
* @return true if element to insert is a custom element
*/
public boolean isCustom() {
return node.getDescriptionOrNull() instanceof ElementLibrary.ElementTypeDescriptionCustom;
return node.isCustom();
}
/**

View File

@ -101,18 +101,18 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
private static final Icon ICON_HELP = IconCreator.create("help.png");
private final CircuitComponent circuitComponent;
private final ToolTipAction save;
private ToolTipAction doStep;
private ToolTipAction runToBreakAction;
private final ElementLibrary library;
private final ShapeFactory shapeFactory;
private final SavedListener savedListener;
private final JLabel statusLabel;
private final StateManager stateManager = new StateManager();
private final ElementAttributes settings = new ElementAttributes();
private final ScheduledThreadPoolExecutor timerExecuter = new ScheduledThreadPoolExecutor(1);
private final WindowPosManager windowPosManager = new WindowPosManager();
private final InsertHistory insertHistory;
private final boolean isParentsLibrary;
private final boolean isNestedFile;
private ToolTipAction doStep;
private ToolTipAction runToBreakAction;
private File lastFilename;
private File filename;
@ -135,54 +135,53 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
}
/**
* Creates a new instance
* Creates a new instance.
* Used to open a nested circuit.
*
* @param parent the parent component
* @param fileToOpen a file to open
* @param savedListener a listener which is notified if the file is changed on disk
* @param parent the parent component
* @param fileToOpen a file to open
* @param library the library to use, if not null a nested circuit is opened
*/
public Main(Component parent, File fileToOpen, SavedListener savedListener, ElementLibrary library) {
this(parent, fileToOpen, savedListener, library, null);
public Main(Component parent, File fileToOpen, ElementLibrary library) {
this(parent, fileToOpen, library, null);
}
/**
* Creates a new instance
* Creates a new instance.
* Used to show a generated circuit.
*
* @param parent the parent component
* @param circuit circuit to show
*/
public Main(Component parent, Circuit circuit) {
this(parent, null, null, null, circuit);
this(parent, null, null, circuit);
}
/**
* Creates a new instance
*
* @param parent the parent component
* @param fileToOpen a file to open
* @param savedListener a listener which is notified if the file is changed on disk
* @param circuit circuit to show
* @param parent the parent component
* @param fileToOpen a file to open
* @param parentsLibrary the parents library. If not null means opening a nested circuit
* @param circuit circuit to show
*/
private Main(Component parent, File fileToOpen, SavedListener savedListener, ElementLibrary parentsLibrary, Circuit circuit) {
private Main(Component parent, File fileToOpen, ElementLibrary parentsLibrary, Circuit circuit) {
super(Lang.get("digital"));
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setIconImages(IconCreator.createImages("icon32.png", "icon64.png", "icon128.png"));
this.savedListener = savedListener;
isParentsLibrary = parentsLibrary != null;
if (isParentsLibrary) this.library = parentsLibrary;
isNestedFile = parentsLibrary != null;
if (isNestedFile) this.library = parentsLibrary;
else library = new ElementLibrary();
shapeFactory = new ShapeFactory(library, Settings.getInstance().get(Keys.SETTINGS_IEEE_SHAPES));
fileHistory = new FileHistory(this);
final boolean normalMode = savedListener == null;
if (circuit != null) {
circuitComponent = new CircuitComponent(this, library, shapeFactory, savedListener);
circuitComponent = new CircuitComponent(this, library, shapeFactory);
SwingUtilities.invokeLater(() -> circuitComponent.setCircuit(circuit));
} else {
circuitComponent = new CircuitComponent(this, library, shapeFactory, savedListener);
circuitComponent = new CircuitComponent(this, library, shapeFactory);
if (fileToOpen != null) {
SwingUtilities.invokeLater(() -> loadFile(fileToOpen, false));
} else {
@ -193,6 +192,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
}
}
library.addListener(circuitComponent);
getContentPane().add(circuitComponent);
componentOnPane = circuitComponent;
@ -204,7 +205,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
JMenuBar menuBar = new JMenuBar();
JToolBar toolBar = new JToolBar();
save = createFileMenu(menuBar, toolBar, normalMode);
save = createFileMenu(menuBar, toolBar);
toolBar.addSeparator();
createViewMenu(menuBar, toolBar);
@ -236,6 +237,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
timerExecuter.shutdown();
library.removeListener(librarySelector);
library.removeListener(insertHistory);
library.removeListener(circuitComponent);
if (treeModel != null)
library.removeListener(treeModel);
}
@ -344,12 +346,11 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
/**
* Creates the file menu and adds it to menu and toolbar
*
* @param menuBar the menuBar
* @param toolBar the toolBar
* @param normalMode if false, menu is added in nested mode
* @param menuBar the menuBar
* @param toolBar the toolBar
* @return the save action
*/
private ToolTipAction createFileMenu(JMenuBar menuBar, JToolBar toolBar, boolean normalMode) {
private ToolTipAction createFileMenu(JMenuBar menuBar, JToolBar toolBar) {
ToolTipAction newFile = new ToolTipAction(Lang.get("menu_new"), ICON_NEW) {
@Override
public void actionPerformed(ActionEvent e) {
@ -359,7 +360,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
windowPosManager.closeAll();
}
}
}.setActive(normalMode);
}.setActive(!isNestedFile);
ToolTipAction open = new ToolTipAction(Lang.get("menu_open"), ICON_OPEN) {
@Override
@ -371,33 +372,33 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
}
}
}
}.setActive(normalMode);
}.setActive(!isNestedFile);
ToolTipAction openWin = new ToolTipAction(Lang.get("menu_openWin"), ICON_OPEN_WIN) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showOpenDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
Main m = new Main(Main.this, fc.getSelectedFile(), null, null);
Main m = new Main(Main.this, fc.getSelectedFile(), null);
m.setLocationRelativeTo(Main.this);
m.setVisible(true);
}
}
}.setToolTip(Lang.get("menu_openWin_tt")).setActive(normalMode);
}.setToolTip(Lang.get("menu_openWin_tt")).setActive(!isNestedFile);
JMenu openRecent = new JMenu(Lang.get("menu_openRecent"));
fileHistory.setMenu(openRecent);
openRecent.setEnabled(normalMode);
openRecent.setEnabled(!isNestedFile);
ToolTipAction saveas = new ToolTipAction(Lang.get("menu_saveAs"), ICON_SAVE_AS) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showSaveDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
saveFile(fc.getSelectedFile(), normalMode);
saveFile(fc.getSelectedFile());
}
}
}.setActive(normalMode);
}.setActive(!isNestedFile);
ToolTipAction save = new ToolTipAction(Lang.get("menu_save"), ICON_SAVE) {
@Override
@ -405,7 +406,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
if (filename == null)
saveas.actionPerformed(e);
else
saveFile(filename, normalMode);
saveFile(filename);
}
};
@ -913,14 +914,13 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
}
}
private void saveFile(File filename, boolean toPrefs) {
private void saveFile(File filename) {
filename = checkSuffix(filename, "dig");
try {
circuitComponent.getCircuit().save(filename);
if (savedListener != null)
savedListener.saved(filename);
stoppedState.enter();
setFilename(filename, toPrefs);
setFilename(filename, !isNestedFile);
library.invalidateElement(filename);
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_errorWritingFile")).addCause(e).show();
}
@ -931,12 +931,12 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
this.filename = filename;
if (filename != null) {
this.lastFilename = filename;
if (!isParentsLibrary) library.setFilePath(filename.getParentFile());
if (!isNestedFile) library.setFilePath(filename.getParentFile());
if (toPrefs)
fileHistory.add(filename);
setTitle(filename + " - " + Lang.get("digital"));
} else {
if (!isParentsLibrary) library.setFilePath(null);
if (!isNestedFile) library.setFilePath(null);
setTitle(Lang.get("digital"));
}
} catch (IOException e) {

View File

@ -1,15 +0,0 @@
package de.neemann.digital.gui;
import java.io.File;
/**
* @author hneemann
*/
public interface SavedListener {
/**
* Method is called if file is changed
*
* @param filename the changed file
*/
void saved(File filename);
}

View File

@ -14,10 +14,11 @@ import de.neemann.digital.draw.elements.*;
import de.neemann.digital.draw.graphics.*;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.library.LibraryListener;
import de.neemann.digital.draw.library.LibraryNode;
import de.neemann.digital.draw.shapes.Drawable;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.Main;
import de.neemann.digital.gui.SavedListener;
import de.neemann.digital.gui.sync.NoSync;
import de.neemann.digital.gui.sync.Sync;
import de.neemann.digital.lang.Lang;
@ -50,7 +51,7 @@ import static java.awt.event.InputEvent.CTRL_DOWN_MASK;
*
* @author hneemann
*/
public class CircuitComponent extends JComponent implements Circuit.ChangedListener {
public class CircuitComponent extends JComponent implements Circuit.ChangedListener, LibraryListener {
/**
* The delete icon, also used from {@link de.neemann.digital.gui.components.terminal.TerminalDialog}
*/
@ -62,7 +63,6 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
private final Main parent;
private final ElementLibrary library;
private final SavedListener parentsSavedListener;
private final HashSet<Drawable> highLighted;
private final ToolTipAction deleteAction;
@ -97,10 +97,9 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
* @param library the library used to edit the attributes of the elements
* @param shapeFactory the shapeFactory used for copied elements
*/
public CircuitComponent(Main parent, ElementLibrary library, ShapeFactory shapeFactory, SavedListener parentsSavedListener) {
public CircuitComponent(Main parent, ElementLibrary library, ShapeFactory shapeFactory) {
this.parent = parent;
this.library = library;
this.parentsSavedListener = parentsSavedListener;
highLighted = new HashSet<>();
rotateAction = new AbstractAction(Lang.get("menu_rotate")) {
@ -456,7 +455,7 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
circuit.drawTo(gr, highLighted, modelSync);
highlightedPaintedSize = highLighted.size();
hasChanged = false;
// System.out.println(System.currentTimeMillis() - time); // -agentlib:hprof=cpu=samples
// LOGGER.debug("repaint: " + Long.toString(System.currentTimeMillis() - time) + "ms");
}
g.drawImage(buffer, 0, 0, null);
@ -593,14 +592,7 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
public void actionPerformed(ActionEvent e) {
attributeDialog.dispose();
SwingUtilities.invokeLater(() -> new Main(CircuitComponent.this,
((ElementLibrary.ElementTypeDescriptionCustom) elementType).getFile(),
filename -> {
if (parentsSavedListener != null)
parentsSavedListener.saved(filename);
library.removeElement(filename);
circuit.clearState();
hasChanged();
}, library).setVisible(true));
((ElementLibrary.ElementTypeDescriptionCustom) elementType).getFile(), library).setVisible(true));
}
}.setToolTip(Lang.get("attr_openCircuit_tt")));
}
@ -624,6 +616,13 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
}
}
@Override
public void libraryChanged(LibraryNode node) {
circuit.clearState();
hasChanged = true;
repaint();
}
private class MouseDispatcher extends MouseAdapter implements MouseMotionListener {
private Vector pos;
private boolean isMoved;