diff --git a/src/main/java/de/neemann/digital/draw/library/LibraryNode.java b/src/main/java/de/neemann/digital/draw/library/LibraryNode.java index a4a278348..7f75d42ac 100644 --- a/src/main/java/de/neemann/digital/draw/library/LibraryNode.java +++ b/src/main/java/de/neemann/digital/draw/library/LibraryNode.java @@ -156,4 +156,36 @@ public class LibraryNode implements Iterable { public ElementTypeDescription getDescriptionOrNull() { return description; } + + /** + * get the child with index i + * + * @param i the index + * @return the child + */ + public LibraryNode getChild(int i) { + return children.get(i); + } + + /** + * @return the number of children + */ + public int size() { + return children == null ? 0 : children.size(); + } + + /** + * Returns the index of the gicen child + * + * @param node the node + * @return the nodes index + */ + public int indexOf(LibraryNode node) { + return children.indexOf(node); + } + + @Override + public String toString() { + return translatedName; + } } diff --git a/src/main/java/de/neemann/digital/gui/Main.java b/src/main/java/de/neemann/digital/gui/Main.java index 2d71a2f59..ab083a81f 100644 --- a/src/main/java/de/neemann/digital/gui/Main.java +++ b/src/main/java/de/neemann/digital/gui/Main.java @@ -25,6 +25,7 @@ import de.neemann.digital.gui.components.data.DataSetDialog; import de.neemann.digital.gui.components.expression.ExpressionDialog; import de.neemann.digital.gui.components.table.TableDialog; import de.neemann.digital.gui.components.testing.TestResultDialog; +import de.neemann.digital.gui.components.tree.SelectTree; import de.neemann.digital.gui.remote.DigitalHandler; import de.neemann.digital.gui.remote.RemoteException; import de.neemann.digital.gui.remote.RemoteSever; @@ -109,6 +110,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E private final ElementAttributes settings = new ElementAttributes(); private final ScheduledThreadPoolExecutor timerExecuter = new ScheduledThreadPoolExecutor(1); private final WindowPosManager windowPosManager = new WindowPosManager(); + private final InsertHistory insertHistory; private File lastFilename; private File filename; @@ -123,6 +125,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E private State stoppedState; private RunModelState runModelState; private State runModelMicroState; + private JComponent componentOnPane; private Main() { this(null, null, null, null); @@ -187,6 +190,7 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E } getContentPane().add(circuitComponent); + componentOnPane = circuitComponent; statusLabel = new JLabel(" "); getContentPane().add(statusLabel, BorderLayout.SOUTH); @@ -223,7 +227,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E toolBar.addSeparator(); - menuBar.add(new LibrarySelector(library, shapeFactory).buildMenu(new InsertHistory(toolBar), circuitComponent)); + insertHistory = new InsertHistory(toolBar); + menuBar.add(new LibrarySelector(library, shapeFactory).buildMenu(insertHistory, circuitComponent)); getContentPane().add(toolBar, BorderLayout.NORTH); @@ -287,6 +292,23 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E } }.setToolTip(Lang.get("menu_viewHelp_tt")); + JCheckBoxMenuItem treeCheckBox = new JCheckBoxMenuItem(Lang.get("menu_treeSelect")); + treeCheckBox.setToolTipText(Lang.get("menu_treeSelect_tt")); + treeCheckBox.addActionListener(actionEvent -> { + getContentPane().remove(componentOnPane); + if (treeCheckBox.isSelected()) { + JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + split.setLeftComponent(new JScrollPane(new SelectTree(library, circuitComponent, shapeFactory, insertHistory))); + split.setRightComponent(circuitComponent); + getContentPane().add(split); + componentOnPane = split; + } else { + getContentPane().add(circuitComponent); + componentOnPane = circuitComponent; + } + pack(); + }); + toolBar.add(viewHelp.createJButtonNoText()); toolBar.add(zoomIn.createJButtonNoText()); toolBar.add(zoomOut.createJButtonNoText()); @@ -298,6 +320,8 @@ public class Main extends JFrame implements ClosingWindowListener.ConfirmSave, E view.add(zoomOut.createJMenuItem()); view.add(zoomIn.createJMenuItem()); view.addSeparator(); + view.add(treeCheckBox); + view.addSeparator(); view.add(viewHelp.createJMenuItem()); } 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 e1b9d579e..60b7023c6 100644 --- a/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java +++ b/src/main/java/de/neemann/digital/gui/components/CircuitComponent.java @@ -57,6 +57,8 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe public static final Icon ICON_DELETE = IconCreator.create("delete.png"); private static final String DEL_ACTION = "myDelAction"; private static final String ESC_ACTION = "myEscAction"; + private static final int MOUSE_BORDER_SMALL = 10; + private static final int MOUSE_BORDER_LARGE = 50; private final Main parent; private final ElementLibrary library; @@ -400,6 +402,29 @@ public class CircuitComponent extends JComponent implements Circuit.ChangedListe public void setPartToInsert(VisualElement element) { parent.stopModel(); mouseInsertElement.activate(element); + Point point = MouseInfo.getPointerInfo().getLocation(); + SwingUtilities.convertPointFromScreen(point, this); + if (point.x < MOUSE_BORDER_LARGE || point.x > getWidth() - MOUSE_BORDER_SMALL + || point.y < MOUSE_BORDER_LARGE || point.y > getHeight() - MOUSE_BORDER_SMALL) { + + if (point.x < MOUSE_BORDER_LARGE) + point.x = MOUSE_BORDER_LARGE; + else if (point.x > getWidth() - MOUSE_BORDER_SMALL) + point.x = getWidth() - MOUSE_BORDER_SMALL; + + if (point.y < MOUSE_BORDER_LARGE) + point.y = MOUSE_BORDER_LARGE; + else if (point.y > getHeight() - MOUSE_BORDER_SMALL) + point.y = getHeight() - MOUSE_BORDER_SMALL; + + SwingUtilities.convertPointToScreen(point, this); + + try { + new Robot().mouseMove(point.x, point.y); + } catch (AWTException e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/de/neemann/digital/gui/components/tree/MyTreeModel.java b/src/main/java/de/neemann/digital/gui/components/tree/MyTreeModel.java new file mode 100644 index 000000000..b9919bb96 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/tree/MyTreeModel.java @@ -0,0 +1,77 @@ +package de.neemann.digital.gui.components.tree; + +import de.neemann.digital.draw.library.ElementLibrary; +import de.neemann.digital.draw.library.LibraryListener; +import de.neemann.digital.draw.library.LibraryNode; + +import javax.swing.event.TreeModelEvent; +import javax.swing.event.TreeModelListener; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; +import java.util.ArrayList; + +/** + * TreeModel based on a {@link ElementLibrary} + * Created by hneemann on 25.03.17. + */ +public class MyTreeModel implements TreeModel, LibraryListener { + private final LibraryNode root; + private final ElementLibrary library; + private final ArrayList listeners = new ArrayList<>(); + + MyTreeModel(ElementLibrary library) { + root = library.getRoot(); + this.library = library; + library.addListener(this); + } + + @Override + public Object getRoot() { + return root; + } + + @Override + public Object getChild(Object o, int i) { + return ((LibraryNode) o).getChild(i); + } + + @Override + public int getChildCount(Object o) { + return ((LibraryNode) o).size(); + } + + @Override + public boolean isLeaf(Object o) { + return ((LibraryNode) o).isLeaf(); + } + + @Override + public void valueForPathChanged(TreePath treePath, Object o) { + + } + + @Override + public int getIndexOfChild(Object o, Object o1) { + return ((LibraryNode) o).indexOf((LibraryNode) o1); + } + + @Override + public void addTreeModelListener(TreeModelListener treeModelListener) { + listeners.add(treeModelListener); + } + + @Override + public void removeTreeModelListener(TreeModelListener treeModelListener) { + listeners.remove(treeModelListener); + } + + @Override + public void libraryChanged() { + LibraryNode custom = library.getCustomNode(); + if (custom != null) { + final TreeModelEvent treeModelEvent = new TreeModelEvent(this, new TreePath(new Object[]{root, custom})); + for (TreeModelListener l : listeners) + l.treeStructureChanged(treeModelEvent); + } + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/tree/SelectTree.java b/src/main/java/de/neemann/digital/gui/components/tree/SelectTree.java new file mode 100644 index 000000000..42212f88d --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/tree/SelectTree.java @@ -0,0 +1,55 @@ +package de.neemann.digital.gui.components.tree; + +import de.neemann.digital.core.element.ElementTypeDescription; +import de.neemann.digital.draw.elements.VisualElement; +import de.neemann.digital.draw.library.ElementLibrary; +import de.neemann.digital.draw.library.LibraryNode; +import de.neemann.digital.draw.shapes.ShapeFactory; +import de.neemann.digital.gui.InsertAction; +import de.neemann.digital.gui.InsertHistory; +import de.neemann.digital.gui.components.CircuitComponent; + +import javax.swing.*; +import javax.swing.tree.TreePath; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; + +/** + * Tree to select items + * Created by hneemann on 25.03.17. + */ +public class SelectTree extends JTree { + + /** + * Create a new instance + * + * @param library the library to use + * @param component the component to insert the components to + * @param shapeFactory the shape factory + * @param insertHistory the insert history + */ + public SelectTree(ElementLibrary library, CircuitComponent component, ShapeFactory shapeFactory, InsertHistory insertHistory) { + super(new MyTreeModel(library)); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent mouseEvent) { + TreePath path = getSelectionPath(); + if (path != null && path.getPathCount() > 0) { + LibraryNode node = (LibraryNode) path.getLastPathComponent(); + if (node.isLeaf()) { + try { + ElementTypeDescription d = node.getDescription(); + component.setPartToInsert(new VisualElement(d.getName()).setShapeFactory(shapeFactory)); + insertHistory.add(new InsertAction(node, insertHistory, component, shapeFactory)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + } + }); + + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/tree/SelectTreeDialog.java b/src/main/java/de/neemann/digital/gui/components/tree/SelectTreeDialog.java new file mode 100644 index 000000000..ee84e740c --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/tree/SelectTreeDialog.java @@ -0,0 +1,50 @@ +package de.neemann.digital.gui.components.tree; + +import de.neemann.digital.draw.library.ElementLibrary; +import de.neemann.digital.draw.library.LibraryListener; +import de.neemann.digital.draw.shapes.ShapeFactory; +import de.neemann.digital.gui.InsertHistory; +import de.neemann.digital.gui.Main; +import de.neemann.digital.gui.components.CircuitComponent; +import de.neemann.digital.lang.Lang; + +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Tree Selector for components. + * Created by hneemann on 25.03.17. + */ +public class SelectTreeDialog extends JDialog { + + /** + * Creates a new instance + * + * @param main the main window + * @param library the library to use + * @param component the component to insert the components to + * @param shapeFactory the shape factory + * @param insertHistory the insert history + */ + public SelectTreeDialog(Main main, ElementLibrary library, CircuitComponent component, ShapeFactory shapeFactory, InsertHistory insertHistory) { + super(main, Lang.get("select"), false); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + + JTree tree = new SelectTree(library, component, shapeFactory, insertHistory); + getContentPane().add(new JScrollPane(tree)); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent windowEvent) { + library.removeListener((LibraryListener) tree.getModel()); + } + }); + + pack(); + setSize(getWidth(), main.getHeight()); + setLocation(main.getLocation().x - getWidth(), main.getLocation().y); + + + } +} diff --git a/src/main/java/de/neemann/digital/gui/components/tree/package-info.java b/src/main/java/de/neemann/digital/gui/components/tree/package-info.java new file mode 100644 index 000000000..805f750e3 --- /dev/null +++ b/src/main/java/de/neemann/digital/gui/components/tree/package-info.java @@ -0,0 +1,5 @@ +/** + * Classes implementing a tree selector component + * Created by hneemann on 25.03.17. + */ +package de.neemann.digital.gui.components.tree; diff --git a/src/main/resources/lang/lang_de.xml b/src/main/resources/lang/lang_de.xml index d1d84bafd..6a2ba15c2 100644 --- a/src/main/resources/lang/lang_de.xml +++ b/src/main/resources/lang/lang_de.xml @@ -686,7 +686,8 @@ Es sind nur {1} Variablen erlaubt, es wurden jedoch {2} gefunden. Zeigt den Inhalt der Speicherbausteine an. Einfügen in neues Fenster Der Inhalt der Zwischenablage wird in einem neuen Fenster geöffnet. - Aktuelle Schaltung + Baumansicht der Bauteile + Zeigt am linken Rand des Fensters eine Baumansicht der verfügbaren Bauteile. Digital @@ -727,6 +728,7 @@ Die Icons stammen aus dem Tango Desktop Project. Während der Ausführung der Tests {0} ist ein Fehler aufgetreten! Meldung vom externen Fitter Starten des externen Fitters + Aktuelle Schaltung Ok diff --git a/src/main/resources/lang/lang_en.xml b/src/main/resources/lang/lang_en.xml index f5af9ee74..3dce009e4 100644 --- a/src/main/resources/lang/lang_en.xml +++ b/src/main/resources/lang/lang_en.xml @@ -673,7 +673,8 @@ allowed are {1} variables but {2} are found. Shows the help dialog describing the actual circuit. Memory Shows the content of memory components. - Actual Circuit + Component Tree View + Shows a tree view of available components at the left side. Digital @@ -714,6 +715,7 @@ The icons are taken from the Tango Desktop Project. During the execution of the tests {0} an error has occurred! Message from the external fitter Execution of external fitter + Actual Circuit Ok