diff --git a/src/main/dig/processor/ALU.dig b/src/main/dig/processor/ALU/ALU.dig
similarity index 100%
rename from src/main/dig/processor/ALU.dig
rename to src/main/dig/processor/ALU/ALU.dig
diff --git a/src/main/dig/processor/ASR.dig b/src/main/dig/processor/ALU/ASR.dig
similarity index 100%
rename from src/main/dig/processor/ASR.dig
rename to src/main/dig/processor/ALU/ASR.dig
diff --git a/src/main/dig/processor/LSL.dig b/src/main/dig/processor/ALU/LSL.dig
similarity index 100%
rename from src/main/dig/processor/LSL.dig
rename to src/main/dig/processor/ALU/LSL.dig
diff --git a/src/main/dig/processor/LSR.dig b/src/main/dig/processor/ALU/LSR.dig
similarity index 100%
rename from src/main/dig/processor/LSR.dig
rename to src/main/dig/processor/ALU/LSR.dig
diff --git a/src/main/dig/processor/SWAP.dig b/src/main/dig/processor/ALU/SWAP.dig
similarity index 100%
rename from src/main/dig/processor/SWAP.dig
rename to src/main/dig/processor/ALU/SWAP.dig
diff --git a/src/main/dig/processor/SWAPN.dig b/src/main/dig/processor/ALU/SWAPN.dig
similarity index 100%
rename from src/main/dig/processor/SWAPN.dig
rename to src/main/dig/processor/ALU/SWAPN.dig
diff --git a/src/main/dig/processor/Processor.dig b/src/main/dig/processor/Processor.dig
index e91cf976a..5884d8a9f 100644
--- a/src/main/dig/processor/Processor.dig
+++ b/src/main/dig/processor/Processor.dig
@@ -122,7 +122,7 @@ Single-Cycle CPU.
true
-
+
Branch.dig
@@ -188,6 +188,10 @@ Single-Cycle CPU.
Driver
+
+ rotation
+
+
Bits
16
@@ -197,12 +201,12 @@ Single-Cycle CPU.
true
-
+
PC.dig
-
+
Clock
@@ -477,16 +481,12 @@ Single-Cycle CPU.
Tunnel
-
- rotation
-
-
NetName
stPC
-
+
Tunnel
@@ -528,7 +528,7 @@ Single-Cycle CPU.
abs
-
+
Tunnel
@@ -576,7 +576,7 @@ Single-Cycle CPU.
ALU2D
-
+
Tunnel
@@ -662,7 +662,7 @@ Single-Cycle CPU.
C
-
+
Tunnel
@@ -766,7 +766,7 @@ Single-Cycle CPU.
-
+
@@ -782,16 +782,20 @@ Single-Cycle CPU.
-
+
+
+
+
+
-
-
+
+
-
-
+
+
@@ -813,10 +817,6 @@ Single-Cycle CPU.
-
-
-
-
@@ -829,6 +829,10 @@ Single-Cycle CPU.
+
+
+
+
@@ -854,7 +858,7 @@ Single-Cycle CPU.
-
+
@@ -882,16 +886,20 @@ Single-Cycle CPU.
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
@@ -901,30 +909,30 @@ Single-Cycle CPU.
-
-
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
@@ -933,6 +941,10 @@ Single-Cycle CPU.
+
+
+
+
@@ -954,12 +966,28 @@ Single-Cycle CPU.
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -978,24 +1006,20 @@ Single-Cycle CPU.
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
-
+
+
@@ -1013,26 +1037,6 @@ Single-Cycle CPU.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1066,8 +1070,8 @@ Single-Cycle CPU.
-
-
+
+
@@ -1081,25 +1085,21 @@ Single-Cycle CPU.
-
-
-
-
-
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
@@ -1118,21 +1118,13 @@ Single-Cycle CPU.
-
-
-
-
-
-
+
+
-
-
-
-
@@ -1141,6 +1133,18 @@ Single-Cycle CPU.
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1165,10 +1169,6 @@ Single-Cycle CPU.
-
-
-
-
@@ -1233,6 +1233,14 @@ Single-Cycle CPU.
+
+
+
+
+
+
+
+
diff --git a/src/main/dig/processor/Branch.dig b/src/main/dig/processor/core/Branch.dig
similarity index 100%
rename from src/main/dig/processor/Branch.dig
rename to src/main/dig/processor/core/Branch.dig
diff --git a/src/main/dig/processor/ClockDiv.dig b/src/main/dig/processor/core/ClockDiv.dig
similarity index 100%
rename from src/main/dig/processor/ClockDiv.dig
rename to src/main/dig/processor/core/ClockDiv.dig
diff --git a/src/main/dig/processor/Control.dig b/src/main/dig/processor/core/Control.dig
similarity index 100%
rename from src/main/dig/processor/Control.dig
rename to src/main/dig/processor/core/Control.dig
diff --git a/src/main/dig/processor/ControlIntr.dig b/src/main/dig/processor/core/ControlIntr.dig
similarity index 100%
rename from src/main/dig/processor/ControlIntr.dig
rename to src/main/dig/processor/core/ControlIntr.dig
diff --git a/src/main/dig/processor/ControlTwoCycle.dig b/src/main/dig/processor/core/ControlTwoCycle.dig
similarity index 100%
rename from src/main/dig/processor/ControlTwoCycle.dig
rename to src/main/dig/processor/core/ControlTwoCycle.dig
diff --git a/src/main/dig/processor/Flags.dig b/src/main/dig/processor/core/Flags.dig
similarity index 100%
rename from src/main/dig/processor/Flags.dig
rename to src/main/dig/processor/core/Flags.dig
diff --git a/src/main/dig/processor/FlagsRW.dig b/src/main/dig/processor/core/FlagsRW.dig
similarity index 100%
rename from src/main/dig/processor/FlagsRW.dig
rename to src/main/dig/processor/core/FlagsRW.dig
diff --git a/src/main/dig/processor/ImReg.dig b/src/main/dig/processor/core/ImReg.dig
similarity index 100%
rename from src/main/dig/processor/ImReg.dig
rename to src/main/dig/processor/core/ImReg.dig
diff --git a/src/main/dig/processor/Interrupt.dig b/src/main/dig/processor/core/Interrupt.dig
similarity index 100%
rename from src/main/dig/processor/Interrupt.dig
rename to src/main/dig/processor/core/Interrupt.dig
diff --git a/src/main/dig/processor/Memory.dig b/src/main/dig/processor/core/Memory.dig
similarity index 100%
rename from src/main/dig/processor/Memory.dig
rename to src/main/dig/processor/core/Memory.dig
diff --git a/src/main/dig/processor/PC.dig b/src/main/dig/processor/core/PC.dig
similarity index 100%
rename from src/main/dig/processor/PC.dig
rename to src/main/dig/processor/core/PC.dig
diff --git a/src/main/dig/processor/PCintr.dig b/src/main/dig/processor/core/PCintr.dig
similarity index 100%
rename from src/main/dig/processor/PCintr.dig
rename to src/main/dig/processor/core/PCintr.dig
diff --git a/src/main/dig/processor/Register.dig b/src/main/dig/processor/core/Register.dig
similarity index 100%
rename from src/main/dig/processor/Register.dig
rename to src/main/dig/processor/core/Register.dig
diff --git a/src/main/dig/processor/Rising.dig b/src/main/dig/processor/core/Rising.dig
similarity index 100%
rename from src/main/dig/processor/Rising.dig
rename to src/main/dig/processor/core/Rising.dig
diff --git a/src/main/dig/processor/SignExt.dig b/src/main/dig/processor/core/SignExt.dig
similarity index 100%
rename from src/main/dig/processor/SignExt.dig
rename to src/main/dig/processor/core/SignExt.dig
diff --git a/src/main/java/de/neemann/digital/draw/library/DescriptionCreator.java b/src/main/java/de/neemann/digital/draw/library/DescriptionCreator.java
new file mode 100644
index 000000000..9c3c8db9f
--- /dev/null
+++ b/src/main/java/de/neemann/digital/draw/library/DescriptionCreator.java
@@ -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;
+}
diff --git a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java
index b71df5b63..63ab189ed 100644
--- a/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java
+++ b/src/main/java/de/neemann/digital/draw/library/ElementLibrary.java
@@ -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 {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ElementLibrary.class);
- private final HashMap map = new HashMap<>();
- private ArrayList list = new ArrayList<>();
- private ElementNotFoundNotification elementNotFoundNotification;
+ private final HashMap map = new HashMap<>();
+ private final ArrayList 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
* @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 iterator() {
- HashSet baseElements = new HashSet<>();
- for (ElementContainer ec : list) {
- baseElements.add(ec.getDescription().getName());
- }
- ArrayList custom = new ArrayList<>();
- for (Map.Entry 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 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 iterator() {
+ ArrayList nodes = new ArrayList<>();
+ for (LibraryNode n : getRoot())
+ addToList(nodes, n, "");
+ return nodes.iterator();
+ }
+
+ private void addToList(ArrayList 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
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
* @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;
}
diff --git a/src/main/java/de/neemann/digital/draw/library/LibraryListener.java b/src/main/java/de/neemann/digital/draw/library/LibraryListener.java
new file mode 100644
index 000000000..8ad5e2aad
--- /dev/null
+++ b/src/main/java/de/neemann/digital/draw/library/LibraryListener.java
@@ -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();
+}
diff --git a/src/main/java/de/neemann/digital/draw/library/LibraryNode.java b/src/main/java/de/neemann/digital/draw/library/LibraryNode.java
new file mode 100644
index 000000000..a4a278348
--- /dev/null
+++ b/src/main/java/de/neemann/digital/draw/library/LibraryNode.java
@@ -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 {
+
+ private final ArrayList 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 the type of the visitor
+ * @return the visitor
+ */
+ public 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 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;
+ }
+}
diff --git a/src/main/java/de/neemann/digital/draw/library/Visitor.java b/src/main/java/de/neemann/digital/draw/library/Visitor.java
new file mode 100644
index 000000000..58ba8c4c7
--- /dev/null
+++ b/src/main/java/de/neemann/digital/draw/library/Visitor.java
@@ -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);
+}
diff --git a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java
index 87fb0c91b..b5bad53d9 100644
--- a/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java
+++ b/src/main/java/de/neemann/digital/draw/shapes/ShapeFactory.java
@@ -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),
diff --git a/src/main/java/de/neemann/digital/gui/InsertHistory.java b/src/main/java/de/neemann/digital/gui/InsertHistory.java
index 2b152c86e..28966acb6 100644
--- a/src/main/java/de/neemann/digital/gui/InsertHistory.java
+++ b/src/main/java/de/neemann/digital/gui/InsertHistory.java
@@ -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 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;
diff --git a/src/main/java/de/neemann/digital/gui/LibrarySelector.java b/src/main/java/de/neemann/digital/gui/LibrarySelector.java
index e54355da1..e8d5cf9e0 100644
--- a/src/main/java/de/neemann/digital/gui/LibrarySelector.java
+++ b/src/main/java/de/neemann/digital/gui/LibrarySelector.java
@@ -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 importedElements;
- private HashMap 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 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;
- }
- }
}
diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java
index 844085482..e10373475 100644
--- a/src/main/java/de/neemann/digital/gui/Main.java
+++ b/src/main/java/de/neemann/digital/gui/Main.java
@@ -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);
+ }
}
/**
diff --git a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java
index d380b1c1a..f1481b437 100644
--- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java
+++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java
@@ -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")));
}
diff --git a/src/main/java/de/neemann/gui/ToolTipAction.java b/src/main/java/de/neemann/gui/ToolTipAction.java
index 13eb8cea8..ef392f494 100644
--- a/src/main/java/de/neemann/gui/ToolTipAction.java
+++ b/src/main/java/de/neemann/gui/ToolTipAction.java
@@ -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
*
diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml
index b9dd80446..80c19eadf 100644
--- a/src/main/resources/lang/lang_de.xml
+++ b/src/main/resources/lang/lang_de.xml
@@ -584,8 +584,8 @@ Es sind nur {1} Variablen erlaubt, es wurden jedoch {2} gefunden.
Führt die Schaltung aus, bis ein Stopsignal über ein BRK-Element detektiert wird.
Datei
Hilfe
- Importieren
- Importiert eine Schaltung als verwendbares Bauteil
+ Aktualisieren
+ Aktualisieren des Bauteile Menüs.
Einpassen
Gatterschrittmodus
Startet die Schaltung im Gatterschrittmodus
diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml
index e752a0562..678eaa39c 100644
--- a/src/main/resources/lang/lang_en.xml
+++ b/src/main/resources/lang/lang_en.xml
@@ -571,8 +571,8 @@ allowed are {1} variables but {2} are found.
Runs the circuit until a break is detected by the BRK component.
File
Help
- Import
- Imports a circuit as a usable component!
+ Update
+ Updates the components menu!
Fit to window
Single gate stepping
Runs the circuit in single gate step mode
diff --git a/src/test/java/de/neemann/digital/integration/TestNesting.java b/src/test/java/de/neemann/digital/integration/TestNesting.java
index ec06f5498..8e5a0f69c 100644
--- a/src/test/java/de/neemann/digital/integration/TestNesting.java
+++ b/src/test/java/de/neemann/digital/integration/TestNesting.java
@@ -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);
}
diff --git a/src/test/java/de/neemann/digital/integration/ToBreakRunner.java b/src/test/java/de/neemann/digital/integration/ToBreakRunner.java
index bbf83589e..425a87822 100644
--- a/src/test/java/de/neemann/digital/integration/ToBreakRunner.java
+++ b/src/test/java/de/neemann/digital/integration/ToBreakRunner.java
@@ -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);
diff --git a/src/test/java/de/neemann/digital/lang/TestElemConsistence.java b/src/test/java/de/neemann/digital/lang/TestElemConsistence.java
index 2be2953e4..7b850ff41 100644
--- a/src/test/java/de/neemann/digital/lang/TestElemConsistence.java
+++ b/src/test/java/de/neemann/digital/lang/TestElemConsistence.java
@@ -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) {