From 6dc25b04ea03f82f35a811ac91b2337c167315d8 Mon Sep 17 00:00:00 2001 From: hneemann Date: Sat, 25 Mar 2017 19:59:05 +0100 Subject: [PATCH] refactoring of library system to allow separate component tree view --- src/main/dig/processor/{ => ALU}/ALU.dig | 0 src/main/dig/processor/{ => ALU}/ASR.dig | 0 src/main/dig/processor/{ => ALU}/LSL.dig | 0 src/main/dig/processor/{ => ALU}/LSR.dig | 0 src/main/dig/processor/{ => ALU}/SWAP.dig | 0 src/main/dig/processor/{ => ALU}/SWAPN.dig | 0 src/main/dig/processor/Processor.dig | 212 ++++---- src/main/dig/processor/{ => core}/Branch.dig | 0 .../dig/processor/{ => core}/ClockDiv.dig | 0 src/main/dig/processor/{ => core}/Control.dig | 0 .../dig/processor/{ => core}/ControlIntr.dig | 0 .../processor/{ => core}/ControlTwoCycle.dig | 0 src/main/dig/processor/{ => core}/Flags.dig | 0 src/main/dig/processor/{ => core}/FlagsRW.dig | 0 src/main/dig/processor/{ => core}/ImReg.dig | 0 .../dig/processor/{ => core}/Interrupt.dig | 0 src/main/dig/processor/{ => core}/Memory.dig | 0 src/main/dig/processor/{ => core}/PC.dig | 0 src/main/dig/processor/{ => core}/PCintr.dig | 0 .../dig/processor/{ => core}/Register.dig | 0 src/main/dig/processor/{ => core}/Rising.dig | 0 src/main/dig/processor/{ => core}/SignExt.dig | 0 .../draw/library/DescriptionCreator.java | 20 + .../digital/draw/library/ElementLibrary.java | 464 +++++++++++++----- .../digital/draw/library/LibraryListener.java | 13 + .../digital/draw/library/LibraryNode.java | 159 ++++++ .../neemann/digital/draw/library/Visitor.java | 15 + .../digital/draw/shapes/ShapeFactory.java | 6 +- .../de/neemann/digital/gui/InsertHistory.java | 32 +- .../neemann/digital/gui/LibrarySelector.java | 352 +++---------- .../java/de/neemann/digital/gui/Main.java | 53 +- .../gui/components/CircuitComponent.java | 7 +- .../java/de/neemann/gui/ToolTipAction.java | 17 + src/main/resources/lang/lang_de.xml | 4 +- src/main/resources/lang/lang_en.xml | 4 +- .../digital/integration/TestNesting.java | 3 +- .../digital/integration/ToBreakRunner.java | 3 +- .../digital/lang/TestElemConsistence.java | 3 +- 38 files changed, 814 insertions(+), 553 deletions(-) rename src/main/dig/processor/{ => ALU}/ALU.dig (100%) rename src/main/dig/processor/{ => ALU}/ASR.dig (100%) rename src/main/dig/processor/{ => ALU}/LSL.dig (100%) rename src/main/dig/processor/{ => ALU}/LSR.dig (100%) rename src/main/dig/processor/{ => ALU}/SWAP.dig (100%) rename src/main/dig/processor/{ => ALU}/SWAPN.dig (100%) rename src/main/dig/processor/{ => core}/Branch.dig (100%) rename src/main/dig/processor/{ => core}/ClockDiv.dig (100%) rename src/main/dig/processor/{ => core}/Control.dig (100%) rename src/main/dig/processor/{ => core}/ControlIntr.dig (100%) rename src/main/dig/processor/{ => core}/ControlTwoCycle.dig (100%) rename src/main/dig/processor/{ => core}/Flags.dig (100%) rename src/main/dig/processor/{ => core}/FlagsRW.dig (100%) rename src/main/dig/processor/{ => core}/ImReg.dig (100%) rename src/main/dig/processor/{ => core}/Interrupt.dig (100%) rename src/main/dig/processor/{ => core}/Memory.dig (100%) rename src/main/dig/processor/{ => core}/PC.dig (100%) rename src/main/dig/processor/{ => core}/PCintr.dig (100%) rename src/main/dig/processor/{ => core}/Register.dig (100%) rename src/main/dig/processor/{ => core}/Rising.dig (100%) rename src/main/dig/processor/{ => core}/SignExt.dig (100%) create mode 100644 src/main/java/de/neemann/digital/draw/library/DescriptionCreator.java create mode 100644 src/main/java/de/neemann/digital/draw/library/LibraryListener.java create mode 100644 src/main/java/de/neemann/digital/draw/library/LibraryNode.java create mode 100644 src/main/java/de/neemann/digital/draw/library/Visitor.java 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) {