refactoring of library system to allow separate component tree view

This commit is contained in:
hneemann 2017-03-25 19:59:05 +01:00
parent 84b16602de
commit 6dc25b04ea
38 changed files with 814 additions and 553 deletions

View File

@ -122,7 +122,7 @@ Single-Cycle CPU.</string>
<boolean>true</boolean>
</entry>
</elementAttributes>
<pos x="1220" y="40"/>
<pos x="1180" y="40"/>
</visualElement>
<visualElement>
<elementName>Branch.dig</elementName>
@ -188,6 +188,10 @@ Single-Cycle CPU.</string>
<visualElement>
<elementName>Driver</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="1"/>
</entry>
<entry>
<string>Bits</string>
<int>16</int>
@ -197,12 +201,12 @@ Single-Cycle CPU.</string>
<boolean>true</boolean>
</entry>
</elementAttributes>
<pos x="520" y="120"/>
<pos x="520" y="40"/>
</visualElement>
<visualElement>
<elementName>PC.dig</elementName>
<elementAttributes/>
<pos x="360" y="120"/>
<pos x="400" y="120"/>
</visualElement>
<visualElement>
<elementName>Clock</elementName>
@ -477,16 +481,12 @@ Single-Cycle CPU.</string>
<visualElement>
<elementName>Tunnel</elementName>
<elementAttributes>
<entry>
<string>rotation</string>
<rotation rotation="3"/>
</entry>
<entry>
<string>NetName</string>
<string>stPC</string>
</entry>
</elementAttributes>
<pos x="520" y="160"/>
<pos x="560" y="40"/>
</visualElement>
<visualElement>
<elementName>Tunnel</elementName>
@ -528,7 +528,7 @@ Single-Cycle CPU.</string>
<string>abs</string>
</entry>
</elementAttributes>
<pos x="340" y="160"/>
<pos x="380" y="160"/>
</visualElement>
<visualElement>
<elementName>Tunnel</elementName>
@ -576,7 +576,7 @@ Single-Cycle CPU.</string>
<string>ALU2D</string>
</entry>
</elementAttributes>
<pos x="1260" y="40"/>
<pos x="1220" y="40"/>
</visualElement>
<visualElement>
<elementName>Tunnel</elementName>
@ -662,7 +662,7 @@ Single-Cycle CPU.</string>
<string>C</string>
</entry>
</elementAttributes>
<pos x="340" y="140"/>
<pos x="380" y="140"/>
</visualElement>
<visualElement>
<elementName>Tunnel</elementName>
@ -766,7 +766,7 @@ Single-Cycle CPU.</string>
</visualElements>
<wires>
<wire>
<p1 x="340" y="320"/>
<p1 x="380" y="320"/>
<p2 x="940" y="320"/>
</wire>
<wire>
@ -782,16 +782,20 @@ Single-Cycle CPU.</string>
<p2 x="1360" y="320"/>
</wire>
<wire>
<p1 x="600" y="0"/>
<p1 x="520" y="0"/>
<p2 x="640" y="0"/>
</wire>
<wire>
<p1 x="640" y="0"/>
<p2 x="800" y="0"/>
</wire>
<wire>
<p1 x="1220" y="0"/>
<p2 x="1440" y="0"/>
<p1 x="800" y="0"/>
<p2 x="1180" y="0"/>
</wire>
<wire>
<p1 x="800" y="0"/>
<p2 x="1220" y="0"/>
<p1 x="1180" y="0"/>
<p2 x="1440" y="0"/>
</wire>
<wire>
<p1 x="740" y="480"/>
@ -813,10 +817,6 @@ Single-Cycle CPU.</string>
<p1 x="900" y="160"/>
<p2 x="1080" y="160"/>
</wire>
<wire>
<p1 x="340" y="160"/>
<p2 x="360" y="160"/>
</wire>
<wire>
<p1 x="1160" y="160"/>
<p2 x="1240" y="160"/>
@ -829,6 +829,10 @@ Single-Cycle CPU.</string>
<p1 x="540" y="160"/>
<p2 x="660" y="160"/>
</wire>
<wire>
<p1 x="380" y="160"/>
<p2 x="400" y="160"/>
</wire>
<wire>
<p1 x="640" y="420"/>
<p2 x="660" y="420"/>
@ -854,7 +858,7 @@ Single-Cycle CPU.</string>
<p2 x="1240" y="360"/>
</wire>
<wire>
<p1 x="320" y="360"/>
<p1 x="340" y="360"/>
<p2 x="360" y="360"/>
</wire>
<wire>
@ -882,16 +886,20 @@ Single-Cycle CPU.</string>
<p2 x="840" y="40"/>
</wire>
<wire>
<p1 x="1240" y="40"/>
<p2 x="1260" y="40"/>
<p1 x="1200" y="40"/>
<p2 x="1220" y="40"/>
</wire>
<wire>
<p1 x="880" y="460"/>
<p2 x="900" y="460"/>
<p1 x="540" y="40"/>
<p2 x="560" y="40"/>
</wire>
<wire>
<p1 x="820" y="460"/>
<p2 x="840" y="460"/>
<p1 x="380" y="140"/>
<p2 x="400" y="140"/>
</wire>
<wire>
<p1 x="500" y="140"/>
<p2 x="520" y="140"/>
</wire>
<wire>
<p1 x="880" y="140"/>
@ -901,30 +909,30 @@ Single-Cycle CPU.</string>
<p1 x="760" y="140"/>
<p2 x="800" y="140"/>
</wire>
<wire>
<p1 x="340" y="140"/>
<p2 x="360" y="140"/>
</wire>
<wire>
<p1 x="640" y="140"/>
<p2 x="660" y="140"/>
</wire>
<wire>
<p1 x="460" y="140"/>
<p2 x="480" y="140"/>
</wire>
<wire>
<p1 x="1160" y="140"/>
<p2 x="1220" y="140"/>
<p2 x="1180" y="140"/>
</wire>
<wire>
<p1 x="1280" y="140"/>
<p2 x="1360" y="140"/>
</wire>
<wire>
<p1 x="1220" y="140"/>
<p1 x="1180" y="140"/>
<p2 x="1280" y="140"/>
</wire>
<wire>
<p1 x="880" y="460"/>
<p2 x="900" y="460"/>
</wire>
<wire>
<p1 x="820" y="460"/>
<p2 x="840" y="460"/>
</wire>
<wire>
<p1 x="1000" y="300"/>
<p2 x="1060" y="300"/>
@ -933,6 +941,10 @@ Single-Cycle CPU.</string>
<p1 x="1140" y="300"/>
<p2 x="1160" y="300"/>
</wire>
<wire>
<p1 x="340" y="240"/>
<p2 x="520" y="240"/>
</wire>
<wire>
<p1 x="1340" y="400"/>
<p2 x="1360" y="400"/>
@ -954,12 +966,28 @@ Single-Cycle CPU.</string>
<p2 x="360" y="400"/>
</wire>
<wire>
<p1 x="340" y="80"/>
<p2 x="1220" y="80"/>
<p1 x="380" y="80"/>
<p2 x="1180" y="80"/>
</wire>
<wire>
<p1 x="320" y="240"/>
<p2 x="480" y="240"/>
<p1 x="380" y="180"/>
<p2 x="400" y="180"/>
</wire>
<wire>
<p1 x="1420" y="180"/>
<p2 x="1440" y="180"/>
</wire>
<wire>
<p1 x="1160" y="180"/>
<p2 x="1220" y="180"/>
</wire>
<wire>
<p1 x="1060" y="180"/>
<p2 x="1080" y="180"/>
</wire>
<wire>
<p1 x="560" y="180"/>
<p2 x="660" y="180"/>
</wire>
<wire>
<p1 x="1340" y="340"/>
@ -978,24 +1006,20 @@ Single-Cycle CPU.</string>
<p2 x="840" y="500"/>
</wire>
<wire>
<p1 x="1420" y="180"/>
<p2 x="1440" y="180"/>
<p1 x="500" y="120"/>
<p2 x="520" y="120"/>
</wire>
<wire>
<p1 x="340" y="180"/>
<p2 x="360" y="180"/>
<p1 x="380" y="120"/>
<p2 x="400" y="120"/>
</wire>
<wire>
<p1 x="1160" y="180"/>
<p2 x="1220" y="180"/>
<p1 x="760" y="120"/>
<p2 x="840" y="120"/>
</wire>
<wire>
<p1 x="1060" y="180"/>
<p2 x="1080" y="180"/>
</wire>
<wire>
<p1 x="560" y="180"/>
<p2 x="660" y="180"/>
<p1 x="640" y="120"/>
<p2 x="660" y="120"/>
</wire>
<wire>
<p1 x="500" y="440"/>
@ -1013,26 +1037,6 @@ Single-Cycle CPU.</string>
<p1 x="1000" y="280"/>
<p2 x="1020" y="280"/>
</wire>
<wire>
<p1 x="540" y="120"/>
<p2 x="600" y="120"/>
</wire>
<wire>
<p1 x="760" y="120"/>
<p2 x="840" y="120"/>
</wire>
<wire>
<p1 x="460" y="120"/>
<p2 x="500" y="120"/>
</wire>
<wire>
<p1 x="340" y="120"/>
<p2 x="360" y="120"/>
</wire>
<wire>
<p1 x="600" y="120"/>
<p2 x="660" y="120"/>
</wire>
<wire>
<p1 x="1340" y="380"/>
<p2 x="1360" y="380"/>
@ -1066,8 +1070,8 @@ Single-Cycle CPU.</string>
<p2 x="1280" y="320"/>
</wire>
<wire>
<p1 x="320" y="240"/>
<p2 x="320" y="360"/>
<p1 x="640" y="0"/>
<p2 x="640" y="120"/>
</wire>
<wire>
<p1 x="580" y="380"/>
@ -1081,25 +1085,21 @@ Single-Cycle CPU.</string>
<p1 x="900" y="160"/>
<p2 x="900" y="460"/>
</wire>
<wire>
<p1 x="1220" y="60"/>
<p2 x="1220" y="80"/>
</wire>
<wire>
<p1 x="1220" y="0"/>
<p2 x="1220" y="20"/>
</wire>
<wire>
<p1 x="1220" y="180"/>
<p2 x="1220" y="340"/>
</wire>
<wire>
<p1 x="1220" y="80"/>
<p2 x="1220" y="140"/>
<p1 x="520" y="140"/>
<p2 x="520" y="240"/>
</wire>
<wire>
<p1 x="520" y="140"/>
<p2 x="520" y="160"/>
<p1 x="520" y="60"/>
<p2 x="520" y="120"/>
</wire>
<wire>
<p1 x="520" y="0"/>
<p2 x="520" y="20"/>
</wire>
<wire>
<p1 x="520" y="600"/>
@ -1118,21 +1118,13 @@ Single-Cycle CPU.</string>
<p2 x="400" y="620"/>
</wire>
<wire>
<p1 x="340" y="180"/>
<p2 x="340" y="320"/>
</wire>
<wire>
<p1 x="340" y="80"/>
<p2 x="340" y="120"/>
<p1 x="340" y="240"/>
<p2 x="340" y="360"/>
</wire>
<wire>
<p1 x="340" y="600"/>
<p2 x="340" y="620"/>
</wire>
<wire>
<p1 x="600" y="0"/>
<p2 x="600" y="120"/>
</wire>
<wire>
<p1 x="600" y="600"/>
<p2 x="600" y="620"/>
@ -1141,6 +1133,18 @@ Single-Cycle CPU.</string>
<p1 x="1240" y="160"/>
<p2 x="1240" y="360"/>
</wire>
<wire>
<p1 x="1180" y="60"/>
<p2 x="1180" y="80"/>
</wire>
<wire>
<p1 x="1180" y="0"/>
<p2 x="1180" y="20"/>
</wire>
<wire>
<p1 x="1180" y="80"/>
<p2 x="1180" y="140"/>
</wire>
<wire>
<p1 x="540" y="160"/>
<p2 x="540" y="380"/>
@ -1165,10 +1169,6 @@ Single-Cycle CPU.</string>
<p1 x="1440" y="180"/>
<p2 x="1440" y="360"/>
</wire>
<wire>
<p1 x="480" y="140"/>
<p2 x="480" y="240"/>
</wire>
<wire>
<p1 x="480" y="600"/>
<p2 x="480" y="620"/>
@ -1233,6 +1233,14 @@ Single-Cycle CPU.</string>
<p1 x="440" y="600"/>
<p2 x="440" y="620"/>
</wire>
<wire>
<p1 x="380" y="180"/>
<p2 x="380" y="320"/>
</wire>
<wire>
<p1 x="380" y="80"/>
<p2 x="380" y="120"/>
</wire>
<wire>
<p1 x="380" y="600"/>
<p2 x="380" y="620"/>

View File

@ -0,0 +1,20 @@
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

@ -3,7 +3,7 @@ package de.neemann.digital.draw.library;
import de.neemann.digital.core.arithmetic.*;
import de.neemann.digital.core.arithmetic.Comparator;
import de.neemann.digital.core.basic.*;
import de.neemann.digital.core.element.ElementTypeDescription;
import de.neemann.digital.core.element.*;
import de.neemann.digital.core.flipflops.FlipflopD;
import de.neemann.digital.core.flipflops.FlipflopJK;
import de.neemann.digital.core.flipflops.FlipflopRS;
@ -19,13 +19,18 @@ import de.neemann.digital.core.switching.PFET;
import de.neemann.digital.core.switching.Relay;
import de.neemann.digital.core.switching.Switch;
import de.neemann.digital.core.wiring.*;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.Tunnel;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.data.DummyElement;
import de.neemann.digital.gui.components.graphics.GraphicCard;
import de.neemann.digital.gui.components.terminal.Keyboard;
import de.neemann.digital.gui.components.terminal.Terminal;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
@ -35,110 +40,146 @@ import java.util.*;
* @author hneemann
*/
public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer> {
private static final Logger LOGGER = LoggerFactory.getLogger(ElementLibrary.class);
private final HashMap<String, ElementTypeDescription> map = new HashMap<>();
private ArrayList<ElementContainer> list = new ArrayList<>();
private ElementNotFoundNotification elementNotFoundNotification;
private final HashMap<String, LibraryNode> map = new HashMap<>();
private final ArrayList<LibraryListener> listeners = new ArrayList<>();
private final LibraryNode root;
private ShapeFactory shapeFactory;
private LibraryNode customNode;
private File rootLibraryPath;
/**
* Creates a new instance.
*/
public ElementLibrary() {
String menu = Lang.get("lib_Logic");
add(And.DESCRIPTION, menu);
add(NAnd.DESCRIPTION, menu);
add(Or.DESCRIPTION, menu);
add(NOr.DESCRIPTION, menu);
add(XOr.DESCRIPTION, menu);
add(XNOr.DESCRIPTION, menu);
add(Not.DESCRIPTION, menu);
add(LookUpTable.DESCRIPTION, menu);
add(Delay.DESCRIPTION, menu);
root = new LibraryNode("root");
menu = Lang.get("lib_io");
add(Out.DESCRIPTION, menu);
add(Out.LEDDESCRIPTION, menu);
add(In.DESCRIPTION, menu);
add(Clock.DESCRIPTION, menu);
add(Button.DESCRIPTION, menu);
add(Probe.DESCRIPTION, menu);
add(Out.SEVENDESCRIPTION, menu);
add(Out.SEVENHEXDESCRIPTION, menu);
add(DummyElement.DATADESCRIPTION, menu);
add(DummyElement.TEXTDESCRIPTION, menu);
add(Keyboard.DESCRIPTION, menu);
add(Terminal.DESCRIPTION, menu);
LibraryNode node = new LibraryNode(Lang.get("lib_Logic"));
node.add(And.DESCRIPTION);
node.add(NAnd.DESCRIPTION);
node.add(Or.DESCRIPTION);
node.add(NOr.DESCRIPTION);
node.add(XOr.DESCRIPTION);
node.add(XNOr.DESCRIPTION);
node.add(Not.DESCRIPTION);
node.add(LookUpTable.DESCRIPTION);
node.add(Delay.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_wires");
add(Const.DESCRIPTION, menu);
add(Ground.DESCRIPTION, menu);
add(VDD.DESCRIPTION, menu);
add(Tunnel.DESCRIPTION, menu);
add(Splitter.DESCRIPTION, menu);
add(PullUp.DESCRIPTION, menu);
add(PullDown.DESCRIPTION, menu);
add(Driver.DESCRIPTION, menu);
add(DriverInvSel.DESCRIPTION, menu);
node = new LibraryNode(Lang.get("lib_io"));
node.add(Out.DESCRIPTION);
node.add(Out.LEDDESCRIPTION);
node.add(In.DESCRIPTION);
node.add(Clock.DESCRIPTION);
node.add(Button.DESCRIPTION);
node.add(Probe.DESCRIPTION);
node.add(Out.SEVENDESCRIPTION);
node.add(Out.SEVENHEXDESCRIPTION);
node.add(DummyElement.DATADESCRIPTION);
node.add(DummyElement.TEXTDESCRIPTION);
node.add(Keyboard.DESCRIPTION);
node.add(Terminal.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_mux");
add(Multiplexer.DESCRIPTION, menu);
add(Demultiplexer.DESCRIPTION, menu);
add(Decoder.DESCRIPTION, menu);
node = new LibraryNode(Lang.get("lib_wires"));
node.add(Const.DESCRIPTION);
node.add(Ground.DESCRIPTION);
node.add(VDD.DESCRIPTION);
node.add(Tunnel.DESCRIPTION);
node.add(Splitter.DESCRIPTION);
node.add(PullUp.DESCRIPTION);
node.add(PullDown.DESCRIPTION);
node.add(Driver.DESCRIPTION);
node.add(DriverInvSel.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_flipFlops");
add(FlipflopRS.DESCRIPTION, menu);
add(FlipflopJK.DESCRIPTION, menu);
add(FlipflopD.DESCRIPTION, menu);
add(FlipflopT.DESCRIPTION, menu);
node = new LibraryNode(Lang.get("lib_mux"));
node.add(Multiplexer.DESCRIPTION);
node.add(Demultiplexer.DESCRIPTION);
node.add(Decoder.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_memory");
add(Register.DESCRIPTION, menu);
add(ROM.DESCRIPTION, menu);
add(RAMDualPort.DESCRIPTION, menu);
add(RAMSinglePort.DESCRIPTION, menu);
add(RAMSinglePortSel.DESCRIPTION, menu);
add(GraphicCard.DESCRIPTION, menu);
add(Counter.DESCRIPTION, menu);
node = new LibraryNode(Lang.get("lib_flipFlops"));
node.add(FlipflopRS.DESCRIPTION);
node.add(FlipflopJK.DESCRIPTION);
node.add(FlipflopD.DESCRIPTION);
node.add(FlipflopT.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_arithmetic");
add(Add.DESCRIPTION, menu);
add(Sub.DESCRIPTION, menu);
add(Mul.DESCRIPTION, menu);
add(Comparator.DESCRIPTION, menu);
add(Neg.DESCRIPTION, menu);
add(BitCount.DESCRIPTION, menu);
node = new LibraryNode(Lang.get("lib_memory"));
node.add(Register.DESCRIPTION);
node.add(ROM.DESCRIPTION);
node.add(RAMDualPort.DESCRIPTION);
node.add(RAMSinglePort.DESCRIPTION);
node.add(RAMSinglePortSel.DESCRIPTION);
node.add(GraphicCard.DESCRIPTION);
node.add(Counter.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_cplx");
node = new LibraryNode(Lang.get("lib_arithmetic"));
node.add(Add.DESCRIPTION);
node.add(Sub.DESCRIPTION);
node.add(Mul.DESCRIPTION);
node.add(Comparator.DESCRIPTION);
node.add(Neg.DESCRIPTION);
node.add(BitCount.DESCRIPTION);
root.add(node);
node = new LibraryNode(Lang.get("lib_cplx"));
// add(Diode.DESCRIPTION, menu); // see class DiodeTest for further information
add(DiodeForeward.DESCRIPTION, menu);
add(DiodeBackward.DESCRIPTION, menu);
add(Switch.DESCRIPTION, menu);
add(Relay.DESCRIPTION, menu);
add(NFET.DESCRIPTION, menu);
add(PFET.DESCRIPTION, menu);
add(Reset.DESCRIPTION, menu);
add(Break.DESCRIPTION, menu);
node.add(DiodeForeward.DESCRIPTION);
node.add(DiodeBackward.DESCRIPTION);
node.add(Switch.DESCRIPTION);
node.add(Relay.DESCRIPTION);
node.add(NFET.DESCRIPTION);
node.add(PFET.DESCRIPTION);
node.add(Reset.DESCRIPTION);
node.add(Break.DESCRIPTION);
root.add(node);
menu = Lang.get("lib_test");
add(TestCaseElement.TESTCASEDESCRIPTION, menu);
}
node = new LibraryNode(Lang.get("lib_test"));
node.add(TestCaseElement.TESTCASEDESCRIPTION);
root.add(node);
private void add(ElementTypeDescription description, String treePath) {
String name = description.getName();
map.put(name, description);
list.add(new ElementContainer(description, treePath));
populateNodeMap();
}
/**
* Adds a description to the library
* Sets the shape factory used to import sub circuits
*
* @param description the description
* @param file the file which was loaded
* @param shapeFactory the shape factory
*/
public void addDescription(ElementTypeDescription description, File file) {
map.put(file.getName(), description);
public void setShapeFactory(ShapeFactory shapeFactory) {
this.shapeFactory = shapeFactory;
}
private void populateNodeMap() {
map.clear();
root.traverse(libraryNode -> {
if (libraryNode.isLeaf()) {
map.put(libraryNode.getName(), libraryNode);
}
});
}
/**
* sets the root library path
*
* @param rootLibraryPath the path
* @throws IOException IOException
*/
public void setFilePath(File rootLibraryPath) throws IOException {
if (rootLibraryPath == null) {
if (this.rootLibraryPath != null) {
this.rootLibraryPath = null;
rescanFolder();
}
} else if (!rootLibraryPath.equals(this.rootLibraryPath)) {
this.rootLibraryPath = rootLibraryPath;
rescanFolder();
}
}
/**
* Returns a {@link ElementTypeDescription} by a given name.
@ -149,60 +190,123 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
* @throws ElementNotFoundException ElementNotFoundException
*/
public ElementTypeDescription getElementType(String elementName) throws ElementNotFoundException {
ElementTypeDescription description = map.get(elementName);
if (description != null)
return description;
try {
LibraryNode description = map.get(elementName);
if (description != null)
return description.getDescription();
elementName = elementName.replace("\\", "/"); // effects only some old files!
File file = new File(elementName);
description = map.get(file.getName());
if (description != null)
return description;
if (elementNotFoundNotification != null)
try {
description = elementNotFoundNotification.elementNotFound(file);
} catch (IOException e) {
throw new ElementNotFoundException(Lang.get("msg_errorImportingModel", elementName), e);
// effects only some old files!
elementName = elementName.replace("\\", "/");
if (elementName.contains("/")) {
elementName = new File(elementName).getName();
}
if (description != null)
return description;
description = map.get(elementName);
if (description != null)
return description.getDescription();
if (rootLibraryPath == null)
throw new RuntimeException("no root path set");
rescanFolder();
description = map.get(elementName);
if (description != null)
return description.getDescription();
} catch (IOException e) {
throw new ElementNotFoundException(Lang.get("msg_errorImportingModel"), e);
}
throw new ElementNotFoundException(Lang.get("err_element_N_notFound", elementName));
}
@Override
public Iterator<ElementContainer> iterator() {
HashSet<String> baseElements = new HashSet<>();
for (ElementContainer ec : list) {
baseElements.add(ec.getDescription().getName());
}
ArrayList<ElementContainer> custom = new ArrayList<>();
for (Map.Entry<String, ElementTypeDescription> entry : map.entrySet()) {
if (!baseElements.contains(entry.getValue().getName())) {
custom.add(new ElementContainer(entry.getValue(), Lang.get("menu_custom")));
}
}
private void rescanFolder() {
LOGGER.debug("rescan folder");
if (customNode == null) {
customNode = new LibraryNode(Lang.get("menu_custom"));
root.add(customNode);
} else customNode.removeAll();
if (custom.isEmpty())
return list.iterator();
else {
Collections.sort(custom, (c1, c2) -> c1.getDescription().getTranslatedName().compareToIgnoreCase(c2.getDescription().getTranslatedName()));
custom.addAll(list);
return custom.iterator();
if (rootLibraryPath != null)
scanFolder(rootLibraryPath, customNode);
populateNodeMap();
fireLibraryChanged();
}
private void fireLibraryChanged() {
for (LibraryListener l : listeners)
l.libraryChanged();
}
private void scanFolder(File path, LibraryNode node) {
File[] list = path.listFiles();
if (list != null) {
ArrayList<File> orderedList = new ArrayList<>(Arrays.asList(list));
orderedList.sort((f1, f2) -> f1.getName().compareToIgnoreCase(f2.getName()));
for (File f : orderedList) {
if (f.isDirectory()) {
LibraryNode n = new LibraryNode(f.getName());
scanFolder(f, n);
if (!n.isEmpty())
node.add(n);
}
}
for (File f : orderedList) {
final String name = f.getName();
if (f.isFile() && name.endsWith(".dig"))
node.add(importElement(f));
}
}
}
/**
* Setes the {@link ElementNotFoundNotification} which can be calle if a element is not present.
* Adds a listener to this library
*
* @param elementNotFoundNotification elementNotFoundNotification
* @param listener the listener to add
*/
public void setElementNotFoundNotification(ElementNotFoundNotification elementNotFoundNotification) {
this.elementNotFoundNotification = elementNotFoundNotification;
public void addListener(LibraryListener listener) {
listeners.add(listener);
}
/**
* Removes a listener from this library
*
* @param listener the listener to remove
*/
public void removeListener(LibraryListener listener) {
listeners.remove(listener);
}
@Override
public Iterator<ElementContainer> iterator() {
ArrayList<ElementContainer> nodes = new ArrayList<>();
for (LibraryNode n : getRoot())
addToList(nodes, n, "");
return nodes.iterator();
}
private void addToList(ArrayList<ElementContainer> nodes, LibraryNode node, String path) {
if (node.isLeaf()) {
if (node.isDescriptionLoaded()) {
try {
nodes.add(new ElementContainer(node.getDescription(), path));
} catch (IOException e) {
// can not happen because description is present!
}
}
} else
for (LibraryNode n : node)
addToList(nodes, n, concat(path, node.getName()));
}
private String concat(String path, String name) {
if (path.length() == 0)
return name;
return path + " - " + name;
}
/**
@ -214,6 +318,120 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
map.remove(name.getName());
}
/**
* Updates all entries
*
* @throws IOException IOException
*/
public void updateEntries() throws IOException {
rescanFolder();
}
/**
* @return the root element
*/
public LibraryNode getRoot() {
return root;
}
private LibraryNode importElement(File file) {
return new LibraryNode(file.getName(), () -> {
try {
LOGGER.debug("load element " + file);
Circuit circuit = Circuit.loadCircuit(file, shapeFactory);
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) {
return createShortName(file.getName());
}
private String createShortName(String name) {
if (name.endsWith(".dig")) return name.substring(0, name.length() - 4);
String transName = Lang.getNull("elem_" + name);
if (transName == null)
return name;
else
return transName;
}
/**
* The description of a nested element.
* This is a complete circuit which is used as a element.
*/
public static class ElementTypeDescriptionCustom extends ElementTypeDescription {
private final File file;
private final ElementAttributes attributes;
private String description;
/**
* Creates a new element
*
* @param file the file which is loaded
* @param elementFactory a element factory which is used to create concrete elements if needed
* @param attributes the attributes of the element
* @param inputNames the names of the input signals
*/
public ElementTypeDescriptionCustom(File file, ElementFactory elementFactory, ElementAttributes attributes, PinDescription... inputNames) {
super(file.getName(), elementFactory, inputNames);
this.file = file;
this.attributes = attributes;
setShortName(file.getName());
addAttribute(Keys.ROTATE);
addAttribute(Keys.LABEL);
}
/**
* Returns the filename
* the retuned file is opened if the user wants to modify the element
*
* @return the filename
*/
public File getFile() {
return file;
}
/**
* @return the elements attributes
*/
public ElementAttributes getAttributes() {
return attributes;
}
/**
* Sets a custom description for this field
*
* @param description the description
*/
public void setDescription(String description) {
this.description = description;
}
@Override
public String getDescription(ElementAttributes elementAttributes) {
if (description != null)
return description;
else
return super.getDescription(elementAttributes);
}
}
/**
* Used to store a elements name and its position in the elements menu.
*/
@ -227,7 +445,7 @@ public class ElementLibrary implements Iterable<ElementLibrary.ElementContainer>
* @param typeDescription the elements typeDescription
* @param treePath the elements menu path
*/
public ElementContainer(ElementTypeDescription typeDescription, String treePath) {
ElementContainer(ElementTypeDescription typeDescription, String treePath) {
this.name = typeDescription;
this.treePath = treePath;
}

View File

@ -0,0 +1,13 @@
package de.neemann.digital.draw.library;
/**
* Listener to notify library uses if the library changes
* Created by hneemann on 25.03.17.
*/
public interface LibraryListener {
/**
* called if library changes
*/
void libraryChanged();
}

View File

@ -0,0 +1,159 @@
package de.neemann.digital.draw.library;
import de.neemann.digital.core.element.ElementTypeDescription;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
/**
* A node in the components library
* Created by hneemann on 25.03.17.
*/
public class LibraryNode implements Iterable<LibraryNode> {
private final ArrayList<LibraryNode> children;
private final String translatedName;
private final String name;
private final DescriptionCreator creator;
private ElementTypeDescription description;
/**
* Creates a new node with the given name.
* The node can have children
*
* @param name name of the node
*/
LibraryNode(String name) {
this.name = name;
this.translatedName = name;
this.children = new ArrayList<>();
this.description = null;
this.creator = null;
}
/**
* Creates a new leaf
*
* @param description the description
*/
private LibraryNode(ElementTypeDescription description) {
this.children = null;
this.description = description;
this.name = description.getName();
this.translatedName = description.getTranslatedName();
this.creator = null;
}
/**
* Creates a new leaf
*
* @param name the name of the leaf
* @param creator used to create the {@link ElementTypeDescription} if necessary
*/
LibraryNode(String name, DescriptionCreator creator) {
this.children = null;
this.name = name;
this.translatedName = name;
this.creator = creator;
}
/**
* Adds a node.
* Throws an exception if this node is a leaf
*
* @param node
*/
void add(LibraryNode node) {
children.add(node);
}
void add(ElementTypeDescription node) {
add(new LibraryNode(node));
}
/**
* Traverse the tree
*
* @param v a visitor
* @param <V> the type of the visitor
* @return the visitor
*/
public <V extends Visitor> V traverse(V v) {
v.visit(this);
if (children != null) {
for (LibraryNode tn : children)
tn.traverse(v);
}
return v;
}
/**
* @return trie if this is a leaf
*/
public boolean isLeaf() {
return description != null || creator != null;
}
/**
* @return true if the description is already loaded
*/
public boolean isDescriptionLoaded() {
return description != null;
}
/**
* Returns the description of the element
*
* @return the description
* @throws IOException IOException
*/
public ElementTypeDescription getDescription() throws IOException {
if (description == null)
description = creator.createDescription();
return description;
}
/**
* @return the translated name of the element
*/
public String getTranslatedName() {
return translatedName;
}
/**
* @return the name od id of this element
*/
public String getName() {
return name;
}
@Override
public Iterator<LibraryNode> iterator() {
return children.iterator();
}
/**
* all children are removed
*/
public void removeAll() {
children.clear();
}
/**
* @return true if this node is empty
*/
public boolean isEmpty() {
if (isLeaf())
return false;
return children.isEmpty();
}
/**
* @return returns the description if present, null otherwise
*/
public ElementTypeDescription getDescriptionOrNull() {
return description;
}
}

View File

@ -0,0 +1,15 @@
package de.neemann.digital.draw.library;
/**
* Visits all elements of the elements tree
* Created by hneemann on 25.03.17.
*/
public interface Visitor {
/**
* Called on every node
*
* @param libraryNode the node
*/
void visit(LibraryNode libraryNode);
}

View File

@ -23,7 +23,6 @@ import de.neemann.digital.draw.shapes.ieee.IEEEAndShape;
import de.neemann.digital.draw.shapes.ieee.IEEENotShape;
import de.neemann.digital.draw.shapes.ieee.IEEEOrShape;
import de.neemann.digital.draw.shapes.ieee.IEEEXOrShape;
import de.neemann.digital.gui.LibrarySelector;
import de.neemann.digital.gui.components.data.DummyElement;
import de.neemann.digital.lang.Lang;
import de.neemann.digital.testing.TestCaseElement;
@ -57,6 +56,7 @@ public final class ShapeFactory {
*/
public ShapeFactory(ElementLibrary library, boolean ieee) {
this.library = library;
library.setShapeFactory(this);
if (ieee) {
map.put(And.DESCRIPTION.getName(), (attributes, inputs, outputs) -> new IEEEAndShape(inputs, outputs, false));
map.put(NAnd.DESCRIPTION.getName(), (attributes, inputs, outputs) -> new IEEEAndShape(inputs, outputs, true));
@ -136,8 +136,8 @@ public final class ShapeFactory {
throw new NodeException(Lang.get("err_noShapeFoundFor_N", elementName));
else {
ElementTypeDescription pt = library.getElementType(elementName);
if (pt instanceof LibrarySelector.ElementTypeDescriptionCustom) {
LibrarySelector.ElementTypeDescriptionCustom customDescr = (LibrarySelector.ElementTypeDescriptionCustom) pt;
if (pt instanceof ElementLibrary.ElementTypeDescriptionCustom) {
ElementLibrary.ElementTypeDescriptionCustom customDescr = (ElementLibrary.ElementTypeDescriptionCustom) pt;
return new GenericShape(
pt.getShortName(),
pt.getInputDescription(elementAttributes),

View File

@ -3,6 +3,7 @@ package de.neemann.digital.gui;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.Iterator;
/**
* The InsertHistory puts the most frequently used elements to the toolbar of the main window.
@ -41,15 +42,20 @@ public class InsertHistory {
bar.add(wrapper);
if (wrappers.size() > MAX_ICONS) {
int oldest = findOldestIndex();
wrapper = wrappers.get(oldest);
bar.remove(wrapper.componentPosition);
for (int i = oldest; i < wrappers.size(); i++)
wrappers.get(i).componentPosition--;
removeWrapperFromBar(wrappers.get(oldest));
wrappers.remove(oldest);
}
}
}
private void removeWrapperFromBar(WrapperAction wrapper) {
final int position = wrapper.componentPosition;
bar.remove(position);
for (WrapperAction w : wrappers)
if (w.componentPosition > position)
w.componentPosition--;
}
private int findOldestIndex() {
int found = -1;
int oldestTime = mainTime;
@ -70,8 +76,24 @@ public class InsertHistory {
return false;
}
private final class WrapperAction extends AbstractAction {
/**
* remove custom components
*/
public void removeCustom() {
Iterator<WrapperAction> it = wrappers.iterator();
while (it.hasNext()) {
WrapperAction w = it.next();
if (w.action instanceof LibrarySelector.InsertAction) {
if (((LibrarySelector.InsertAction) w.action).isCustom()) {
removeWrapperFromBar(w);
it.remove();
}
}
}
bar.revalidate();
}
private final class WrapperAction extends AbstractAction {
private final AbstractAction action;
private int componentPosition;
private int time;

View File

@ -1,13 +1,10 @@
package de.neemann.digital.gui;
import de.neemann.digital.core.element.*;
import de.neemann.digital.draw.elements.Circuit;
import de.neemann.digital.draw.elements.PinException;
import de.neemann.digital.draw.elements.VisualElement;
import de.neemann.digital.draw.graphics.Vector;
import de.neemann.digital.draw.library.CustomElement;
import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundNotification;
import de.neemann.digital.draw.library.LibraryListener;
import de.neemann.digital.draw.library.LibraryNode;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.components.CircuitComponent;
import de.neemann.digital.gui.state.State;
@ -15,16 +12,10 @@ import de.neemann.digital.lang.Lang;
import de.neemann.gui.ErrorMessage;
import de.neemann.gui.StringUtils;
import de.neemann.gui.ToolTipAction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* The LibrarySelector is responsible for building the menu used to select items for adding them to the circuit.
@ -32,22 +23,17 @@ import java.util.HashMap;
*
* @author hneemann
*/
public class LibrarySelector implements ElementNotFoundNotification {
private static final Logger LOGGER = LoggerFactory.getLogger(LibrarySelector.class);
public class LibrarySelector implements LibraryListener {
private final ElementLibrary library;
private final ShapeFactory shapeFactory;
private final State elementState;
private File filePath;
private JMenu customMenu;
private JMenu componentsMenu;
private InsertHistory insertHistory;
private CircuitComponent circuitComponent;
private ArrayList<ImportedItem> importedElements;
private HashMap<String, File> treeFileMap;
/**
* Creates a new library selector.
* the elementState is used to seht the window to the elemetEdit mode if a new element is added to the circuit.
* the elementState is used to set the window to the elementEdit mode if a new element is added to the circuit.
*
* @param library the library to select elements from
* @param shapeFactory The shape factory
@ -55,10 +41,9 @@ public class LibrarySelector implements ElementNotFoundNotification {
*/
public LibrarySelector(ElementLibrary library, ShapeFactory shapeFactory, State elementState) {
this.library = library;
library.addListener(this);
this.shapeFactory = shapeFactory;
this.elementState = elementState;
library.setElementNotFoundNotification(this);
importedElements = new ArrayList<>();
}
/**
@ -73,64 +58,47 @@ public class LibrarySelector implements ElementNotFoundNotification {
public JMenu buildMenu(InsertHistory insertHistory, CircuitComponent circuitComponent) {
this.insertHistory = insertHistory;
this.circuitComponent = circuitComponent;
JMenu parts = new JMenu(Lang.get("menu_elements"));
componentsMenu = new JMenu(Lang.get("menu_elements"));
libraryChanged();
customMenu = new JMenu(Lang.get("menu_custom"));
parts.add(customMenu);
customMenu.add(new ToolTipAction(Lang.get("menu_import")) {
@Override
public void actionPerformed(ActionEvent e) {
JFileChooser fc = new JFileChooser(filePath);
fc.setFileFilter(new FileNameExtensionFilter("Circuit", "dig"));
if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
try {
Imported imp = importElement(fc.getSelectedFile());
if (imp != null) {
VisualElement visualElement = new VisualElement(imp.description.getName()).setPos(new Vector(10, 10)).setShapeFactory(shapeFactory);
elementState.enter();
circuitComponent.setPartToInsert(visualElement);
insertHistory.add(imp.insertAction);
}
} catch (IOException e1) {
new ErrorMessage(Lang.get("msg_errorImportingModel")).addCause(e1).show();
}
}
}
}.setToolTip(Lang.get("menu_import_tt")).createJMenuItem());
customMenu.add(new ToolTipAction(Lang.get("menu_refresh")) {
@Override
public void actionPerformed(ActionEvent e) {
removeCustomElements();
}
}.setToolTip(Lang.get("menu_refresh_tt")).createJMenuItem());
JMenu subMenu = null;
String lastPath = null;
for (ElementLibrary.ElementContainer elementContainer : library) {
String path = elementContainer.getTreePath();
if (!path.equals(lastPath)) {
subMenu = new JMenu(path);
parts.add(subMenu);
lastPath = path;
}
subMenu.add(new InsertAction(elementContainer.getDescription(), insertHistory, circuitComponent)
.setToolTip(createToolTipText(elementContainer.getDescription().getName()))
.createJMenuItem());
}
return parts;
return componentsMenu;
}
/**
* removes all custom elements
*/
public void removeCustomElements() {
for (ImportedItem item : importedElements) {
library.removeElement(item.file);
customMenu.remove(item.menuEntry);
@Override
public void libraryChanged() {
componentsMenu.removeAll();
for (LibraryNode n : library.getRoot())
addComponents(componentsMenu, n);
insertHistory.removeCustom();
JMenuItem m = componentsMenu.getItem(componentsMenu.getItemCount() - 1);
if (m instanceof JMenu) {
((JMenu) m).add(new ToolTipAction(Lang.get("menu_import")) {
@Override
public void actionPerformed(ActionEvent e) {
try {
library.updateEntries();
} catch (IOException ex) {
SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorImportingModel")).addCause(ex));
}
}
}.setToolTip(Lang.get("menu_import_tt")).createJMenuItem());
}
}
private void addComponents(JMenu parts, LibraryNode node) {
if (node.isLeaf()) {
parts.add(new InsertAction(node, insertHistory, circuitComponent)
.setToolTip(createToolTipText(node.getName()))
.createJMenuItem());
} else {
JMenu subMenu = new JMenu(node.getName());
for (LibraryNode child : node)
addComponents(subMenu, child);
parts.add(subMenu);
}
}
@ -138,232 +106,50 @@ public class LibrarySelector implements ElementNotFoundNotification {
return StringUtils.textToHTML(Lang.getNull("elem_" + elementName + "_tt"));
}
/**
* sets the file path which is used to load missing nested elements
*
* @param filePath the file path
*/
public void setFilePath(File filePath) {
treeFileMap = null;
this.filePath = filePath;
}
@Override
public ElementTypeDescription elementNotFound(File file) throws IOException {
// check if there is a file with the given name in the current directory
// if so, load this file!
File primary = new File(filePath, file.getName());
if (primary.exists())
file = primary;
// check if there is a file with the given name below the current directory
// if so, load this file!
File f = getFileFromTree(file.getName());
if (f != null)
return importElement(f).description;
// than check if the exact given file exists
if (file.exists())
return importElement(file).description;
// could not find the given file
throw new IOException(Lang.get("err_couldNotFindIncludedFile_N0", file));
}
private File getFileFromTree(String name) throws IOException {
if (treeFileMap == null) {
treeFileMap = new HashMap<>();
populateTreeFileMap(treeFileMap, filePath);
} else {
if (!treeFileMap.containsKey(name)) { // if file not in map rescan folder
treeFileMap.clear();
populateTreeFileMap(treeFileMap, filePath);
}
}
return treeFileMap.get(name);
}
private void populateTreeFileMap(HashMap<String, File> map, File path) throws IOException {
File[] list = path.listFiles();
if (list != null) {
for (File f : list) {
final String name = f.getName();
if (f.isFile() && name.endsWith(".dig")) {
if (map.containsKey(name))
throw new IOException(Lang.get("err_file_N0_ExistsTwiceBelow_N1", name, filePath));
map.put(name, f);
}
if (f.isDirectory())
populateTreeFileMap(map, f);
}
}
}
private static String getActionName(ElementTypeDescription typeDescription) {
if (typeDescription instanceof ElementTypeDescriptionCustom)
return typeDescription.getShortName();
else
return typeDescription.getTranslatedName();
}
private final class InsertAction extends ToolTipAction {
private final String name;
final class InsertAction extends ToolTipAction {
private final LibraryNode node;
private final InsertHistory insertHistory;
private final CircuitComponent circuitComponent;
private InsertAction(ElementTypeDescription typeDescription, InsertHistory insertHistory, CircuitComponent circuitComponent) {
super(getActionName(typeDescription), new VisualElement(typeDescription.getName()).setShapeFactory(shapeFactory).createIcon(75));
this.name = typeDescription.getName();
private InsertAction(LibraryNode node, InsertHistory insertHistory, CircuitComponent circuitComponent) {
super(node.getTranslatedName(), createIcon(node, shapeFactory));
this.node = node;
this.insertHistory = insertHistory;
this.circuitComponent = circuitComponent;
}
@Override
public void actionPerformed(ActionEvent e) {
VisualElement visualElement = new VisualElement(name).setPos(new Vector(10, 10)).setShapeFactory(shapeFactory);
VisualElement visualElement = new VisualElement(node.getName()).setPos(new Vector(10, 10)).setShapeFactory(shapeFactory);
elementState.enter();
circuitComponent.setPartToInsert(visualElement);
if (getIcon() == null) {
try {
node.getDescription();
setIcon(createIcon(node, shapeFactory));
} catch (IOException ex) {
SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorImportingModel")).addCause(ex));
}
}
insertHistory.add(this);
}
}
private Imported importElement(File file) throws IOException {
try {
LOGGER.debug("load element " + file);
Circuit circuit = Circuit.loadCircuit(file, shapeFactory);
ElementTypeDescriptionCustom description =
new ElementTypeDescriptionCustom(file,
attributes -> new CustomElement(circuit, library, file),
circuit.getAttributes(), circuit.getInputNames());
description.setShortName(createShortName(file));
library.addDescription(description, file);
InsertAction insertAction = new InsertAction(description, insertHistory, circuitComponent);
String descriptionText = circuit.getAttributes().get(Keys.DESCRIPTION);
if (descriptionText != null && descriptionText.length() > 0) {
insertAction.setToolTip(StringUtils.textToHTML(descriptionText));
description.setDescription(descriptionText);
}
JMenuItem menuEntry = insertAction.createJMenuItem();
ImportedItem item = findImportedItem(file);
if (item != null) {
if (customMenu != null) {
customMenu.remove(item.menuEntry);
}
importedElements.remove(item);
}
importedElements.add(new ImportedItem(file, menuEntry));
if (customMenu != null)
customMenu.add(menuEntry);
return new Imported(description, insertAction);
} catch (PinException e) {
throw new IOException(e);
public boolean isCustom() {
return node.getDescriptionOrNull() instanceof ElementLibrary.ElementTypeDescriptionCustom;
}
}
private ImportedItem findImportedItem(File file) {
for (ImportedItem i : importedElements) {
if (i.file.equals(file))
return i;
private static ImageIcon createIcon(LibraryNode node, ShapeFactory shapeFactory) {
// don't load the description if only the icon is needed
// create action without an icon instead
if (node.isDescriptionLoaded()) {
try {
return new VisualElement(node.getDescription().getName()).setShapeFactory(shapeFactory).createIcon(75);
} catch (IOException ex) {
SwingUtilities.invokeLater(new ErrorMessage(Lang.get("msg_errorImportingModel")).addCause(ex));
}
}
return null;
}
private String createShortName(File file) {
return createShortName(file.getName());
}
private String createShortName(String name) {
if (name.endsWith(".dig")) return name.substring(0, name.length() - 4);
String transName = Lang.getNull("elem_" + name);
if (transName == null)
return name;
else
return transName;
}
private final static class ImportedItem {
private final File file;
private final JMenuItem menuEntry;
private ImportedItem(File file, JMenuItem menuEntry) {
this.file = file;
this.menuEntry = menuEntry;
}
}
/**
* The description of a nested element.
* This is a complete circuit which is used as a element.
*/
public static class ElementTypeDescriptionCustom extends ElementTypeDescription {
private final File file;
private final ElementAttributes attributes;
private String description;
/**
* Creates a new element
*
* @param file the file which is loaded
* @param elementFactory a element factory which is used to create concrete elements if needed
* @param attributes the attributes of the element
* @param inputNames the names of the input signals
*/
public ElementTypeDescriptionCustom(File file, ElementFactory elementFactory, ElementAttributes attributes, PinDescription... inputNames) {
super(file.getPath().replace('\\', '/'), elementFactory, inputNames);
this.file = file;
this.attributes = attributes;
setShortName(file.getName());
addAttribute(Keys.ROTATE);
addAttribute(Keys.LABEL);
}
/**
* Returns the filename
* the retuned file is opened if the user wants to modify the element
*
* @return the filename
*/
public File getFile() {
return file;
}
/**
* @return the elements attributes
*/
public ElementAttributes getAttributes() {
return attributes;
}
/**
* Sets a custom description for this field
*
* @param description the description
*/
public void setDescription(String description) {
this.description = description;
}
@Override
public String getDescription(ElementAttributes elementAttributes) {
if (description != null)
return description;
else
return super.getDescription(elementAttributes);
}
}
private final static class Imported {
private final ElementTypeDescription description;
private final InsertAction insertAction;
private Imported(ElementTypeDescription description, InsertAction insertAction) {
this.description = description;
this.insertAction = insertAction;
}
}
}

View File

@ -127,7 +127,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
private State runModelMicroState;
private Main() {
this(null, null, null);
this(null, null, null, null);
}
/**
@ -137,8 +137,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
* @param fileToOpen a file to open
* @param savedListener a listener which is notified if the file is changed on disk
*/
public Main(Component parent, File fileToOpen, SavedListener savedListener) {
this(parent, fileToOpen, savedListener, null);
public Main(Component parent, File fileToOpen, SavedListener savedListener, ElementLibrary library) {
this(parent, fileToOpen, savedListener, library, null);
}
/**
@ -148,7 +148,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
* @param circuit circuit to show
*/
public Main(Component parent, Circuit circuit) {
this(parent, null, null, circuit);
this(parent, null, null, null, circuit);
}
/**
@ -159,13 +159,14 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
* @param savedListener a listener which is notified if the file is changed on disk
* @param circuit circuit to show
*/
private Main(Component parent, File fileToOpen, SavedListener savedListener, Circuit circuit) {
private Main(Component parent, File fileToOpen, SavedListener savedListener, 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;
library = new ElementLibrary();
if (parentsLibrary == null) library = new ElementLibrary();
else this.library = parentsLibrary;
shapeFactory = new ShapeFactory(library, Settings.getInstance().get(Keys.SETTINGS_IEEE_SHAPES));
fileHistory = new FileHistory(this);
@ -276,8 +277,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
if (file == null)
file = new File(name);
try {
LibrarySelector.ElementTypeDescriptionCustom description =
new LibrarySelector.ElementTypeDescriptionCustom(file,
ElementLibrary.ElementTypeDescriptionCustom description =
new ElementLibrary.ElementTypeDescriptionCustom(file,
attributes -> new CustomElement(circuit, library, filename),
circuit.getAttributes(), circuit.getInputNames());
description.setShortName(name);
@ -319,7 +320,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
setFilename(null, true);
circuitComponent.setCircuit(new Circuit());
windowPosManager.closeAll();
librarySelector.removeCustomElements();
}
}
}.setActive(normalMode);
@ -330,7 +330,6 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
if (ClosingWindowListener.checkForSave(Main.this, Main.this)) {
JFileChooser fc = getJFileChooser(lastFilename);
if (fc.showOpenDialog(Main.this) == JFileChooser.APPROVE_OPTION) {
librarySelector.removeCustomElements();
loadFile(fc.getSelectedFile(), true);
}
}
@ -342,7 +341,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
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);
Main m = new Main(Main.this, fc.getSelectedFile(), null, null);
m.setLocationRelativeTo(Main.this);
m.setVisible(true);
}
@ -399,7 +398,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
/**
* Creates the edit menu
*
* @param menuBar the menu bar
* @param menuBar the menu bar
*/
private void createEditMenu(JMenuBar menuBar) {
JMenu edit = new JMenu(Lang.get("menu_edit"));
@ -496,7 +495,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
/**
* Creates the start menu
* @param menuBar the menu bar
*
* @param menuBar the menu bar
* @param toolBar the tool bar
*/
private void createStartMenu(JMenuBar menuBar, JToolBar toolBar) {
@ -820,14 +820,13 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
@Override
public void open(File file) {
if (ClosingWindowListener.checkForSave(Main.this, Main.this)) {
librarySelector.removeCustomElements();
loadFile(file, true);
}
}
private void loadFile(File filename, boolean toPrefs) {
try {
librarySelector.setFilePath(filename.getParentFile());
library.setFilePath(filename.getParentFile());
Circuit circ = Circuit.loadCircuit(filename, shapeFactory);
circuitComponent.setCircuit(circ);
stoppedState.enter();
@ -855,15 +854,21 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E
}
private void setFilename(File filename, boolean toPrefs) {
this.filename = filename;
if (filename != null) {
this.lastFilename = filename;
librarySelector.setFilePath(filename.getParentFile());
if (toPrefs)
fileHistory.add(filename);
setTitle(filename + " - " + Lang.get("digital"));
} else
setTitle(Lang.get("digital"));
try {
this.filename = filename;
if (filename != null) {
this.lastFilename = filename;
library.setFilePath(filename.getParentFile());
if (toPrefs)
fileHistory.add(filename);
setTitle(filename + " - " + Lang.get("digital"));
} else {
library.setFilePath(null);
setTitle(Lang.get("digital"));
}
} catch (IOException e) {
new ErrorMessage(Lang.get("msg_errorReadingFile")).addCause(e).show(this);
}
}
/**

View File

@ -16,7 +16,6 @@ import de.neemann.digital.draw.library.ElementLibrary;
import de.neemann.digital.draw.library.ElementNotFoundException;
import de.neemann.digital.draw.shapes.Drawable;
import de.neemann.digital.draw.shapes.ShapeFactory;
import de.neemann.digital.gui.LibrarySelector;
import de.neemann.digital.gui.Main;
import de.neemann.digital.gui.SavedListener;
import de.neemann.digital.gui.sync.NoSync;
@ -562,20 +561,20 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe
Point p = new Point(e.getX(), e.getY());
SwingUtilities.convertPointToScreen(p, CircuitComponent.this);
AttributeDialog attributeDialog = new AttributeDialog(this, p, list, vp.getElementAttributes());
if (elementType instanceof LibrarySelector.ElementTypeDescriptionCustom) {
if (elementType instanceof ElementLibrary.ElementTypeDescriptionCustom) {
attributeDialog.addButton(Lang.get("attr_openCircuitLabel"), new ToolTipAction(Lang.get("attr_openCircuit")) {
@Override
public void actionPerformed(ActionEvent e) {
attributeDialog.dispose();
SwingUtilities.invokeLater(() -> new Main(CircuitComponent.this,
((LibrarySelector.ElementTypeDescriptionCustom) elementType).getFile(),
((ElementLibrary.ElementTypeDescriptionCustom) elementType).getFile(),
filename -> {
if (parentsSavedListener != null)
parentsSavedListener.saved(filename);
library.removeElement(filename);
circuit.clearState();
hasChanged();
}).setVisible(true));
}, library).setVisible(true));
}
}.setToolTip(Lang.get("attr_openCircuit_tt")));
}

View File

@ -31,6 +31,23 @@ public abstract class ToolTipAction extends AbstractAction {
this.icon = icon;
}
/**
* sets the icon
*
* @param icon the icon to set
*/
public void setIcon(Icon icon) {
putValue("SmallIcon", icon);
this.icon = icon;
}
/**
* @return the icon
*/
public Icon getIcon() {
return icon;
}
/**
* Sets the tool tip text
*

View File

@ -584,8 +584,8 @@ Es sind nur {1} Variablen erlaubt, es wurden jedoch {2} gefunden.</string>
<string name="menu_fast_tt">Führt die Schaltung aus, bis ein Stopsignal über ein BRK-Element detektiert wird.</string>
<string name="menu_file">Datei</string>
<string name="menu_help">Hilfe</string>
<string name="menu_import">Importieren</string>
<string name="menu_import_tt">Importiert eine Schaltung als verwendbares Bauteil</string>
<string name="menu_import">Aktualisieren</string>
<string name="menu_import_tt">Aktualisieren des Bauteile Menüs.</string>
<string name="menu_maximize">Einpassen</string>
<string name="menu_micro">Gatterschrittmodus</string>
<string name="menu_micro_tt">Startet die Schaltung im Gatterschrittmodus</string>

View File

@ -571,8 +571,8 @@ allowed are {1} variables but {2} are found.</string>
<string name="menu_fast_tt">Runs the circuit until a break is detected by the BRK component.</string>
<string name="menu_file">File</string>
<string name="menu_help">Help</string>
<string name="menu_import">Import</string>
<string name="menu_import_tt">Imports a circuit as a usable component!</string>
<string name="menu_import">Update</string>
<string name="menu_import_tt">Updates the components menu!</string>
<string name="menu_maximize">Fit to window</string>
<string name="menu_micro">Single gate stepping</string>
<string name="menu_micro_tt">Runs the circuit in single gate step mode</string>

View File

@ -64,8 +64,7 @@ public class TestNesting extends TestCase {
private TestExecuter createTestExecuterForNesting(String file) throws IOException, NodeException, PinException, ElementNotFoundException {
ElementLibrary library = new ElementLibrary();
LibrarySelector librarySelector = new LibrarySelector(library, new ShapeFactory(library), null);
librarySelector.setFilePath(new File(Resources.getRoot(), "dig"));
library.setFilePath(new File(Resources.getRoot(), "dig"));
return TestExecuter.createFromFile(file, library);
}

View File

@ -70,10 +70,9 @@ public class ToBreakRunner {
private ToBreakRunner(File filename, boolean doInit) throws IOException, PinException, NodeException, ElementNotFoundException {
library = new ElementLibrary();
library.setFilePath(filename.getParentFile());
ShapeFactory shapeFactory = new ShapeFactory(library);
circuit = Circuit.loadCircuit(filename, shapeFactory);
LibrarySelector librarySelector = new LibrarySelector(library, shapeFactory, null);
librarySelector.setFilePath(filename.getParentFile());
ModelCreator md = new ModelCreator(circuit, library);
model = md.createModel(false);

View File

@ -23,6 +23,7 @@ public class TestElemConsistence extends TestCase {
* @throws NodeException
*/
public void testConsistence() throws NodeException, PinException {
/*
ElementLibrary library = new ElementLibrary();
for (ElementLibrary.ElementContainer e : library) {
ElementTypeDescription etd = e.getDescription();
@ -37,7 +38,7 @@ public class TestElemConsistence extends TestCase {
checkPins(key, etd.getInputDescription(new ElementAttributes()));
checkPins(key, etd.getOutputDescriptions(new ElementAttributes()));
}
}
}*/ fail();
}
private void checkPins(String key, PinDescriptions pins) {