mirror of
https://github.com/hneemann/Digital.git
synced 2025-09-13 06:49:36 -04:00
fixed an ugly bug concerning mutable key default values
This commit is contained in:
parent
e0ebd0e0ae
commit
e59be72194
@ -28,9 +28,9 @@
|
|||||||
<sodipodi:namedview
|
<sodipodi:namedview
|
||||||
showgrid="true"
|
showgrid="true"
|
||||||
id="namedview4"
|
id="namedview4"
|
||||||
inkscape:zoom="3.5454545"
|
inkscape:zoom="3.5090909"
|
||||||
inkscape:cx="71.064102"
|
inkscape:cx="100.47927"
|
||||||
inkscape:cy="87.435899"
|
inkscape:cy="110"
|
||||||
inkscape:window-width="1680"
|
inkscape:window-width="1680"
|
||||||
inkscape:window-height="993"
|
inkscape:window-height="993"
|
||||||
inkscape:window-x="0"
|
inkscape:window-x="0"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
id="grid2" />
|
id="grid2" />
|
||||||
</sodipodi:namedview>
|
</sodipodi:namedview>
|
||||||
<path
|
<path
|
||||||
style="fill:#ffffb4;fill-opacity:0.78431373;stroke:#000000;stroke-width:3"
|
style="fill:#ffffb4;fill-opacity:0.78431373;stroke:#000000;stroke-width:4"
|
||||||
d="M 0,-30 80,10 V 90 L 0,130 V 60 L 30,50 0,40 Z"
|
d="M 0,-30 80,10 V 90 L 0,130 V 60 L 30,50 0,40 Z"
|
||||||
id="rect6"
|
id="rect6"
|
||||||
inkscape:connector-curvature="0"
|
inkscape:connector-curvature="0"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
<circle
|
<circle
|
||||||
style="fill:#0000b2"
|
style="fill:#0000b2"
|
||||||
r="3"
|
r="3"
|
||||||
cy="100.10256"
|
cy="79.794868"
|
||||||
cx="0.28205127"
|
cx="0.28205127"
|
||||||
id="pin+:B" />
|
id="pin+:B" />
|
||||||
<circle
|
<circle
|
||||||
@ -71,7 +71,7 @@
|
|||||||
<circle
|
<circle
|
||||||
style="fill:#0000b2"
|
style="fill:#0000b2"
|
||||||
r="3"
|
r="3"
|
||||||
cy="120.07693"
|
cy="99.769234"
|
||||||
cx="-0.2820513"
|
cx="-0.2820513"
|
||||||
id="pin+:Ci" />
|
id="pin+:Ci" />
|
||||||
<circle
|
<circle
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@ -17,20 +17,22 @@ import java.io.File;
|
|||||||
public class Key<VALUE> {
|
public class Key<VALUE> {
|
||||||
private final String key;
|
private final String key;
|
||||||
private final VALUE def;
|
private final VALUE def;
|
||||||
|
private final Default<VALUE> defFactory;
|
||||||
private final String langKey;
|
private final String langKey;
|
||||||
private boolean groupEditAllowed = false;
|
private boolean groupEditAllowed = false;
|
||||||
private Key dependsOn;
|
private Key dependsOn;
|
||||||
private CheckEnabled checkEnabled;
|
private CheckEnabled checkEnabled;
|
||||||
|
private boolean isSecondary;
|
||||||
|
private boolean requiresRestart = false;
|
||||||
|
|
||||||
// Both values are always null in digital.
|
// Both values are always null in digital.
|
||||||
// Both are only used within a custom implemented component.
|
// Both are only used within a custom implemented component.
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private boolean isSecondary;
|
|
||||||
private boolean requiresRestart = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Key
|
* Creates a new Key.
|
||||||
|
* Use this constructor only if the def value is not mutable!
|
||||||
*
|
*
|
||||||
* @param key the key
|
* @param key the key
|
||||||
* @param def the default value
|
* @param def the default value
|
||||||
@ -41,6 +43,23 @@ public class Key<VALUE> {
|
|||||||
if (def == null)
|
if (def == null)
|
||||||
throw new NullPointerException();
|
throw new NullPointerException();
|
||||||
this.def = def;
|
this.def = def;
|
||||||
|
this.defFactory = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Key.
|
||||||
|
* Use this constructor if the def value is mutable!
|
||||||
|
*
|
||||||
|
* @param key the key
|
||||||
|
* @param defFactory the factory to create a default value
|
||||||
|
*/
|
||||||
|
public Key(String key, Default<VALUE> defFactory) {
|
||||||
|
this.key = key;
|
||||||
|
langKey = "key_" + key.replace(" ", "");
|
||||||
|
if (defFactory == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
this.def = null;
|
||||||
|
this.defFactory = defFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,14 +87,17 @@ public class Key<VALUE> {
|
|||||||
* @return the default value of this key
|
* @return the default value of this key
|
||||||
*/
|
*/
|
||||||
public VALUE getDefault() {
|
public VALUE getDefault() {
|
||||||
|
if (def != null)
|
||||||
return def;
|
return def;
|
||||||
|
else
|
||||||
|
return defFactory.createDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The values class
|
* @return The values class
|
||||||
*/
|
*/
|
||||||
public Class getValueClass() {
|
public Class getValueClass() {
|
||||||
return def.getClass();
|
return getDefault().getClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -490,4 +512,18 @@ public class Key<VALUE> {
|
|||||||
*/
|
*/
|
||||||
boolean isEnabled(T t);
|
boolean isEnabled(T t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to provide a default value if the value is mutable.
|
||||||
|
*
|
||||||
|
* @param <VALUE> the type of the value
|
||||||
|
*/
|
||||||
|
public interface Default<VALUE> {
|
||||||
|
/**
|
||||||
|
* Called to create a new default value.
|
||||||
|
*
|
||||||
|
* @return the default value
|
||||||
|
*/
|
||||||
|
VALUE createDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ public final class Keys {
|
|||||||
* the data key for memory
|
* the data key for memory
|
||||||
*/
|
*/
|
||||||
public static final Key<DataField> DATA
|
public static final Key<DataField> DATA
|
||||||
= new Key<>("Data", DataField.DEFAULT);
|
= new Key<>("Data", DataField::new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* flag for flipping selector pos in muxers, decoders and drivers
|
* flag for flipping selector pos in muxers, decoders and drivers
|
||||||
@ -602,7 +602,7 @@ public final class Keys {
|
|||||||
* contains the input inverter config
|
* contains the input inverter config
|
||||||
*/
|
*/
|
||||||
public static final Key<InverterConfig> INVERTER_CONFIG
|
public static final Key<InverterConfig> INVERTER_CONFIG
|
||||||
= new Key<>("inverterConfig", new InverterConfig());
|
= new Key<>("inverterConfig", new InverterConfig.Builder().build());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background Color of nested circuits
|
* Background Color of nested circuits
|
||||||
@ -661,7 +661,7 @@ public final class Keys {
|
|||||||
* The manager which contains all the roms data
|
* The manager which contains all the roms data
|
||||||
*/
|
*/
|
||||||
public static final Key<ROMManger> ROMMANAGER
|
public static final Key<ROMManger> ROMMANAGER
|
||||||
= new Key<>("romContent", ROMManger.EMPTY).setSecondary();
|
= new Key<>("romContent", ROMManger::new).setSecondary();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -707,7 +707,7 @@ public final class Keys {
|
|||||||
* Shape used to represent a visual element
|
* Shape used to represent a visual element
|
||||||
*/
|
*/
|
||||||
public static final Key<CustomShapeDescription> CUSTOM_SHAPE
|
public static final Key<CustomShapeDescription> CUSTOM_SHAPE
|
||||||
= new Key<>("customShape", CustomShapeDescription.EMPTY)
|
= new Key<>("customShape", new CustomShapeDescription.Builder().build())
|
||||||
.setSecondary()
|
.setSecondary()
|
||||||
.setDependsOn(SHAPE_TYPE, st -> st.equals(CustomCircuitShapeType.CUSTOM));
|
.setDependsOn(SHAPE_TYPE, st -> st.equals(CustomCircuitShapeType.CUSTOM));
|
||||||
|
|
||||||
|
@ -17,15 +17,17 @@ import java.util.Arrays;
|
|||||||
*/
|
*/
|
||||||
public class DataField implements HGSArray {
|
public class DataField implements HGSArray {
|
||||||
|
|
||||||
/***
|
|
||||||
* Simple default data field
|
|
||||||
*/
|
|
||||||
public static final DataField DEFAULT = new DataField(0);
|
|
||||||
|
|
||||||
private long[] data;
|
private long[] data;
|
||||||
|
|
||||||
private final transient ArrayList<DataListener> listeners = new ArrayList<>();
|
private final transient ArrayList<DataListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new DataField of size 0
|
||||||
|
*/
|
||||||
|
public DataField() {
|
||||||
|
this(0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new DataField
|
* Creates a new DataField
|
||||||
*
|
*
|
||||||
@ -174,6 +176,13 @@ public class DataField implements HGSArray {
|
|||||||
return data.length;
|
return data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the data field is empty
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return trim() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a listener to this DataField
|
* Adds a listener to this DataField
|
||||||
*
|
*
|
||||||
@ -247,4 +256,17 @@ public class DataField implements HGSArray {
|
|||||||
public long[] getData() {
|
public long[] getData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
DataField dataField = (DataField) o;
|
||||||
|
return Arrays.equals(data, dataField.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Arrays.hashCode(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
package de.neemann.digital.core.memory;
|
package de.neemann.digital.core.memory;
|
||||||
|
|
||||||
import de.neemann.digital.core.Node;
|
import de.neemann.digital.core.*;
|
||||||
import de.neemann.digital.core.NodeException;
|
|
||||||
import de.neemann.digital.core.ObservableValue;
|
|
||||||
import de.neemann.digital.core.ObservableValues;
|
|
||||||
import de.neemann.digital.core.element.Element;
|
import de.neemann.digital.core.element.Element;
|
||||||
import de.neemann.digital.core.element.ElementAttributes;
|
import de.neemann.digital.core.element.ElementAttributes;
|
||||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||||
@ -40,6 +37,7 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface
|
|||||||
|
|
||||||
private final int bits;
|
private final int bits;
|
||||||
private final int addrBits;
|
private final int addrBits;
|
||||||
|
private final ElementAttributes attr;
|
||||||
private final int size;
|
private final int size;
|
||||||
private final String label;
|
private final String label;
|
||||||
private final ObservableValue dataOut;
|
private final ObservableValue dataOut;
|
||||||
@ -65,6 +63,7 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface
|
|||||||
*/
|
*/
|
||||||
public EEPROM(ElementAttributes attr) {
|
public EEPROM(ElementAttributes attr) {
|
||||||
super(true);
|
super(true);
|
||||||
|
this.attr = attr;
|
||||||
bits = attr.get(Keys.BITS);
|
bits = attr.get(Keys.BITS);
|
||||||
addrBits = attr.get(Keys.ADDR_BITS);
|
addrBits = attr.get(Keys.ADDR_BITS);
|
||||||
size = 1 << addrBits;
|
size = 1 << addrBits;
|
||||||
@ -77,6 +76,14 @@ public class EEPROM extends Node implements Element, RAMInterface, ROMInterface
|
|||||||
isProgramMemory = attr.get(Keys.IS_PROGRAM_MEMORY);
|
isProgramMemory = attr.get(Keys.IS_PROGRAM_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerNodes(Model model) {
|
||||||
|
super.registerNodes(model);
|
||||||
|
|
||||||
|
if (memory.isEmpty())
|
||||||
|
model.addObserver(event -> attr.set(Keys.DATA, memory), ModelEvent.STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setInputs(ObservableValues inputs) throws NodeException {
|
public void setInputs(ObservableValues inputs) throws NodeException {
|
||||||
addrIn = inputs.get(0).checkBits(addrBits, this).addObserverToValue(this);
|
addrIn = inputs.get(0).checkBits(addrBits, this).addObserverToValue(this);
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.neemann.digital.core.memory;
|
package de.neemann.digital.core.memory;
|
||||||
|
|
||||||
|
import de.neemann.digital.core.Model;
|
||||||
|
import de.neemann.digital.core.ModelEvent;
|
||||||
import de.neemann.digital.core.element.ElementAttributes;
|
import de.neemann.digital.core.element.ElementAttributes;
|
||||||
import de.neemann.digital.core.element.ElementTypeDescription;
|
import de.neemann.digital.core.element.ElementTypeDescription;
|
||||||
import de.neemann.digital.core.element.Keys;
|
import de.neemann.digital.core.element.Keys;
|
||||||
@ -33,6 +35,9 @@ public class EEPROMDualPort extends RAMDualPort implements ROMInterface {
|
|||||||
.addAttribute(Keys.LABEL)
|
.addAttribute(Keys.LABEL)
|
||||||
.addAttribute(Keys.DATA);
|
.addAttribute(Keys.DATA);
|
||||||
|
|
||||||
|
private final ElementAttributes attr;
|
||||||
|
private DataField memory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
*
|
*
|
||||||
@ -40,11 +45,21 @@ public class EEPROMDualPort extends RAMDualPort implements ROMInterface {
|
|||||||
*/
|
*/
|
||||||
public EEPROMDualPort(ElementAttributes attr) {
|
public EEPROMDualPort(ElementAttributes attr) {
|
||||||
super(attr);
|
super(attr);
|
||||||
|
this.attr = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected DataField createDataField(ElementAttributes attr, int size) {
|
protected DataField createDataField(ElementAttributes attr, int size) {
|
||||||
return attr.get(Keys.DATA);
|
memory = attr.get(Keys.DATA);
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerNodes(Model model) {
|
||||||
|
super.registerNodes(model);
|
||||||
|
|
||||||
|
if (memory.isEmpty())
|
||||||
|
model.addObserver(event -> attr.set(Keys.DATA, memory), ModelEvent.STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,15 +10,12 @@ import de.neemann.digital.core.Node;
|
|||||||
import de.neemann.digital.core.memory.DataField;
|
import de.neemann.digital.core.memory.DataField;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Manager to manage all necessary rom images
|
* The Manager to manage all necessary rom images
|
||||||
*/
|
*/
|
||||||
public class ROMManger {
|
public class ROMManger {
|
||||||
/**
|
|
||||||
* The empty instance
|
|
||||||
*/
|
|
||||||
public static final ROMManger EMPTY = new ROMManger();
|
|
||||||
|
|
||||||
private final HashMap<String, DataField> roms;
|
private final HashMap<String, DataField> roms;
|
||||||
|
|
||||||
@ -35,6 +32,8 @@ public class ROMManger {
|
|||||||
* @param model the mode to use
|
* @param model the mode to use
|
||||||
*/
|
*/
|
||||||
public void applyTo(Model model) {
|
public void applyTo(Model model) {
|
||||||
|
if (roms == null)
|
||||||
|
return;
|
||||||
for (Node n : model.findNode(n -> n instanceof ROMInterface)) {
|
for (Node n : model.findNode(n -> n instanceof ROMInterface)) {
|
||||||
ROMInterface rom = (ROMInterface) n;
|
ROMInterface rom = (ROMInterface) n;
|
||||||
DataField data = roms.get(rom.getLabel());
|
DataField data = roms.get(rom.getLabel());
|
||||||
@ -65,19 +64,23 @@ public class ROMManger {
|
|||||||
roms.put(label, data);
|
roms.put(label, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return returns EMPTY it this ROMManager is empty, this otherwise
|
|
||||||
*/
|
|
||||||
public ROMManger getMinimized() {
|
|
||||||
if (roms.isEmpty())
|
|
||||||
return EMPTY;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if no ROM's are stored
|
* @return true if no ROM's are stored
|
||||||
*/
|
*/
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return roms.isEmpty();
|
return roms.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ROMManger romManger = (ROMManger) o;
|
||||||
|
return Objects.equals(roms, romManger.roms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(roms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,29 +15,12 @@ import java.util.Objects;
|
|||||||
/**
|
/**
|
||||||
* Manages the input inverting of a component
|
* Manages the input inverting of a component
|
||||||
*/
|
*/
|
||||||
public class InverterConfig implements HGSMap {
|
public final class InverterConfig implements HGSMap {
|
||||||
|
|
||||||
private HashSet<String> inputs;
|
private HashSet<String> inputs;
|
||||||
|
|
||||||
/**
|
private InverterConfig(HashSet<String> inputs) {
|
||||||
* Creates a new instance.
|
this.inputs = inputs;
|
||||||
* No input is inverted.
|
|
||||||
*/
|
|
||||||
public InverterConfig() {
|
|
||||||
inputs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a signal to invert
|
|
||||||
*
|
|
||||||
* @param name the signale
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public InverterConfig add(String name) {
|
|
||||||
if (inputs == null)
|
|
||||||
inputs = new HashSet<>();
|
|
||||||
inputs.add(name);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,4 +112,35 @@ public class InverterConfig implements HGSMap {
|
|||||||
|
|
||||||
return inputs.contains(key);
|
return inputs.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder to create InverterConfig instances
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private HashSet<String> inputs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a signal to invert
|
||||||
|
*
|
||||||
|
* @param name the signale
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder add(String name) {
|
||||||
|
if (inputs == null)
|
||||||
|
inputs = new HashSet<>();
|
||||||
|
inputs.add(name);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creats the instance
|
||||||
|
*
|
||||||
|
* @return the created instance
|
||||||
|
*/
|
||||||
|
public InverterConfig build() {
|
||||||
|
return new InverterConfig(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ public final class ShapeFactory {
|
|||||||
return new LayoutShape(customDescr, elementAttributes);
|
return new LayoutShape(customDescr, elementAttributes);
|
||||||
case CUSTOM:
|
case CUSTOM:
|
||||||
final CustomShapeDescription customShapeDescription = customDescr.getAttributes().get(Keys.CUSTOM_SHAPE);
|
final CustomShapeDescription customShapeDescription = customDescr.getAttributes().get(Keys.CUSTOM_SHAPE);
|
||||||
if (customShapeDescription != CustomShapeDescription.EMPTY)
|
if (!customShapeDescription.isEmpty())
|
||||||
return new CustomShape(customShapeDescription, elementAttributes.getLabel(),
|
return new CustomShape(customShapeDescription, elementAttributes.getLabel(),
|
||||||
pt.getInputDescription(elementAttributes),
|
pt.getInputDescription(elementAttributes),
|
||||||
pt.getOutputDescriptions(elementAttributes));
|
pt.getOutputDescriptions(elementAttributes));
|
||||||
|
@ -24,95 +24,19 @@ import java.util.Iterator;
|
|||||||
/**
|
/**
|
||||||
* Is intended to be stored in a file.
|
* Is intended to be stored in a file.
|
||||||
*/
|
*/
|
||||||
public class CustomShapeDescription implements Iterable<CustomShapeDescription.Holder> {
|
public final class CustomShapeDescription implements Iterable<CustomShapeDescription.Holder> {
|
||||||
/**
|
|
||||||
* The default empty shape instance
|
|
||||||
*/
|
|
||||||
public static final CustomShapeDescription EMPTY = new CustomShapeDescription();
|
|
||||||
|
|
||||||
private HashMap<String, Pin> pins;
|
private final HashMap<String, Pin> pins;
|
||||||
private ArrayList<Holder> drawables;
|
private final ArrayList<Holder> drawables;
|
||||||
private TextHolder label;
|
private final TextHolder label;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
*/
|
*/
|
||||||
public CustomShapeDescription() {
|
private CustomShapeDescription(HashMap<String, Pin> pins, ArrayList<Holder> drawables, TextHolder label) {
|
||||||
pins = new HashMap<>();
|
this.pins = pins;
|
||||||
drawables = new ArrayList<>();
|
this.drawables = drawables;
|
||||||
}
|
this.label = label;
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a pin to this shape description
|
|
||||||
*
|
|
||||||
* @param name the name of the pin
|
|
||||||
* @param pos the pins position
|
|
||||||
* @param showLabel if true the label of the pin is shown
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public CustomShapeDescription addPin(String name, Vector pos, boolean showLabel) {
|
|
||||||
pins.put(name, new Pin(pos, showLabel));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a polygon to the shape
|
|
||||||
*
|
|
||||||
* @param p1 starting point of the line
|
|
||||||
* @param p2 ending point of the line
|
|
||||||
* @param thickness the line thickness
|
|
||||||
* @param color the color to use
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public CustomShapeDescription addLine(Vector p1, Vector p2, int thickness, Color color) {
|
|
||||||
drawables.add(new LineHolder(p1, p2, thickness, color));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a circle to the shape
|
|
||||||
*
|
|
||||||
* @param p1 upper left corner of the circles bounding box
|
|
||||||
* @param p2 lower right corner of the circles bounding box
|
|
||||||
* @param thickness the line thickness
|
|
||||||
* @param color the color to use
|
|
||||||
* @param filled true if filled
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public CustomShapeDescription addCircle(Vector p1, Vector p2, int thickness, Color color, boolean filled) {
|
|
||||||
drawables.add(new CircleHolder(p1, p2, thickness, color, filled));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a polygon to the shape
|
|
||||||
*
|
|
||||||
* @param poly the polygon to add
|
|
||||||
* @param thickness the line thickness
|
|
||||||
* @param color the color to use
|
|
||||||
* @param filled true if filled
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public CustomShapeDescription addPolygon(Polygon poly, int thickness, Color color, boolean filled) {
|
|
||||||
drawables.add(new PolygonHolder(poly, thickness, filled, color));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a text to the shape
|
|
||||||
*
|
|
||||||
* @param p1 position
|
|
||||||
* @param p2 second position to determin the base line orientation
|
|
||||||
* @param text the text to draw
|
|
||||||
* @param orientation the orientation of the text
|
|
||||||
* @param size the font size
|
|
||||||
* @param color the text color
|
|
||||||
* @return this for chained calls
|
|
||||||
*/
|
|
||||||
public CustomShapeDescription addText(Vector p1, Vector p2, String text, Orientation orientation, int size, Color color) {
|
|
||||||
drawables.add(new TextHolder(p1, p2, text, orientation, size, color));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,19 +79,6 @@ public class CustomShapeDescription implements Iterable<CustomShapeDescription.H
|
|||||||
return pins.size();
|
return pins.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the label positioning info.
|
|
||||||
*
|
|
||||||
* @param pos0 pos0
|
|
||||||
* @param pos1 pos1
|
|
||||||
* @param textOrientation textOrientation
|
|
||||||
* @param fontSize fontSize
|
|
||||||
* @param filled filled
|
|
||||||
*/
|
|
||||||
public void setLabel(Vector pos0, Vector pos1, Orientation textOrientation, int fontSize, Color filled) {
|
|
||||||
label = new TextHolder(pos0, pos1, "", textOrientation, fontSize, filled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the TextHolder used to draw the label, maybe null
|
* @return the TextHolder used to draw the label, maybe null
|
||||||
*/
|
*/
|
||||||
@ -182,6 +93,13 @@ public class CustomShapeDescription implements Iterable<CustomShapeDescription.H
|
|||||||
return pins.values();
|
return pins.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if shape is empty
|
||||||
|
*/
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return drawables.isEmpty() && pins.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the compatibility of this shape to the given circuit
|
* Checks the compatibility of this shape to the given circuit
|
||||||
*
|
*
|
||||||
@ -201,6 +119,26 @@ public class CustomShapeDescription implements Iterable<CustomShapeDescription.H
|
|||||||
throw new PinException(Lang.get("err_morePinsDefinedInSVGAsNeeded"));
|
throw new PinException(Lang.get("err_morePinsDefinedInSVGAsNeeded"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Two CustomShapeDescriptions are equal if and only if they are both empty!
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
CustomShapeDescription customShapeDescription = (CustomShapeDescription) o;
|
||||||
|
|
||||||
|
return customShapeDescription.isEmpty() && isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if (isEmpty())
|
||||||
|
return 0;
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
private interface Transformable {
|
private interface Transformable {
|
||||||
void transform(Transform tr);
|
void transform(Transform tr);
|
||||||
}
|
}
|
||||||
@ -423,4 +361,117 @@ public class CustomShapeDescription implements Iterable<CustomShapeDescription.H
|
|||||||
pos = pos.transform(tr).round();
|
pos = pos.transform(tr).round();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to build a custom shape description
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
private final HashMap<String, Pin> pins;
|
||||||
|
private final ArrayList<Holder> drawables;
|
||||||
|
private TextHolder label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new builder
|
||||||
|
*/
|
||||||
|
public Builder() {
|
||||||
|
pins = new HashMap<>();
|
||||||
|
drawables = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the label positioning info.
|
||||||
|
*
|
||||||
|
* @param pos0 pos0
|
||||||
|
* @param pos1 pos1
|
||||||
|
* @param textOrientation textOrientation
|
||||||
|
* @param fontSize fontSize
|
||||||
|
* @param filled filled
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder setLabel(Vector pos0, Vector pos1, Orientation textOrientation, int fontSize, Color filled) {
|
||||||
|
label = new TextHolder(pos0, pos1, "", textOrientation, fontSize, filled);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a pin to this shape description
|
||||||
|
*
|
||||||
|
* @param name the name of the pin
|
||||||
|
* @param pos the pins position
|
||||||
|
* @param showLabel if true the label of the pin is shown
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder addPin(String name, Vector pos, boolean showLabel) {
|
||||||
|
pins.put(name, new Pin(pos, showLabel));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a polygon to the shape
|
||||||
|
*
|
||||||
|
* @param p1 starting point of the line
|
||||||
|
* @param p2 ending point of the line
|
||||||
|
* @param thickness the line thickness
|
||||||
|
* @param color the color to use
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder addLine(Vector p1, Vector p2, int thickness, Color color) {
|
||||||
|
drawables.add(new LineHolder(p1, p2, thickness, color));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a circle to the shape
|
||||||
|
*
|
||||||
|
* @param p1 upper left corner of the circles bounding box
|
||||||
|
* @param p2 lower right corner of the circles bounding box
|
||||||
|
* @param thickness the line thickness
|
||||||
|
* @param color the color to use
|
||||||
|
* @param filled true if filled
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder addCircle(Vector p1, Vector p2, int thickness, Color color, boolean filled) {
|
||||||
|
drawables.add(new CircleHolder(p1, p2, thickness, color, filled));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a polygon to the shape
|
||||||
|
*
|
||||||
|
* @param poly the polygon to add
|
||||||
|
* @param thickness the line thickness
|
||||||
|
* @param color the color to use
|
||||||
|
* @param filled true if filled
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder addPolygon(Polygon poly, int thickness, Color color, boolean filled) {
|
||||||
|
drawables.add(new PolygonHolder(poly, thickness, filled, color));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a text to the shape
|
||||||
|
*
|
||||||
|
* @param p1 position
|
||||||
|
* @param p2 second position to determin the base line orientation
|
||||||
|
* @param text the text to draw
|
||||||
|
* @param orientation the orientation of the text
|
||||||
|
* @param size the font size
|
||||||
|
* @param color the text color
|
||||||
|
* @return this for chained calls
|
||||||
|
*/
|
||||||
|
public Builder addText(Vector p1, Vector p2, String text, Orientation orientation, int size, Color color) {
|
||||||
|
drawables.add(new TextHolder(p1, p2, text, orientation, size, color));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link CustomShapeDescription}
|
||||||
|
*/
|
||||||
|
public CustomShapeDescription build() {
|
||||||
|
return new CustomShapeDescription(pins, drawables, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,9 +67,10 @@ public class SvgImporter {
|
|||||||
NodeList gList = svg.getElementsByTagName("svg").item(0).getChildNodes();
|
NodeList gList = svg.getElementsByTagName("svg").item(0).getChildNodes();
|
||||||
Context c = new Context();
|
Context c = new Context();
|
||||||
try {
|
try {
|
||||||
CustomShapeDescription csd = new CustomShapeDescription();
|
CustomShapeDescription.Builder builder = new CustomShapeDescription.Builder();
|
||||||
create(csd, gList, c);
|
create(builder, gList, c);
|
||||||
|
|
||||||
|
CustomShapeDescription csd = builder.build();
|
||||||
if (csd.getPinCount() > 0) {
|
if (csd.getPinCount() > 0) {
|
||||||
float xMin = Float.MAX_VALUE;
|
float xMin = Float.MAX_VALUE;
|
||||||
float yMin = Float.MAX_VALUE;
|
float yMin = Float.MAX_VALUE;
|
||||||
@ -86,7 +87,7 @@ public class SvgImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create(CustomShapeDescription csd, NodeList gList, Context c) throws SvgException {
|
private void create(CustomShapeDescription.Builder csd, NodeList gList, Context c) throws SvgException {
|
||||||
for (int i = 0; i < gList.getLength(); i++) {
|
for (int i = 0; i < gList.getLength(); i++) {
|
||||||
final Node node = gList.item(i);
|
final Node node = gList.item(i);
|
||||||
if (node instanceof Element) {
|
if (node instanceof Element) {
|
||||||
@ -99,7 +100,7 @@ public class SvgImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create(CustomShapeDescription csd, Element element, Context parent) throws SvgException {
|
private void create(CustomShapeDescription.Builder csd, Element element, Context parent) throws SvgException {
|
||||||
Context c = new Context(parent, element);
|
Context c = new Context(parent, element);
|
||||||
switch (element.getNodeName()) {
|
switch (element.getNodeName()) {
|
||||||
case "a":
|
case "a":
|
||||||
@ -150,19 +151,19 @@ public class SvgImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawTransformedPolygon(CustomShapeDescription csd, Context c, Polygon polygon) {
|
private void drawTransformedPolygon(CustomShapeDescription.Builder csd, Context c, Polygon polygon) {
|
||||||
if (polygon != null)
|
if (polygon != null)
|
||||||
drawPolygon(csd, c, polygon.transform(c.getTransform()));
|
drawPolygon(csd, c, polygon.transform(c.getTransform()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawPolygon(CustomShapeDescription csd, Context c, Polygon polygon) {
|
private void drawPolygon(CustomShapeDescription.Builder csd, Context c, Polygon polygon) {
|
||||||
if (c.getFilled() != null && polygon.isClosed())
|
if (c.getFilled() != null && polygon.isClosed())
|
||||||
csd.addPolygon(polygon, c.getThickness(), c.getFilled(), true);
|
csd.addPolygon(polygon, c.getThickness(), c.getFilled(), true);
|
||||||
if (c.getStroke() != null)
|
if (c.getStroke() != null)
|
||||||
csd.addPolygon(polygon, c.getThickness(), c.getStroke(), false);
|
csd.addPolygon(polygon, c.getThickness(), c.getStroke(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawRect(CustomShapeDescription csd, Element element, Context c) {
|
private void drawRect(CustomShapeDescription.Builder csd, Element element, Context c) {
|
||||||
VectorInterface size = new VectorFloat(c.getLength(element.getAttribute("width")), c.getLength(element.getAttribute("height")));
|
VectorInterface size = new VectorFloat(c.getLength(element.getAttribute("width")), c.getLength(element.getAttribute("height")));
|
||||||
VectorInterface pos = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y")));
|
VectorInterface pos = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y")));
|
||||||
String rxStr = element.getAttribute("rx");
|
String rxStr = element.getAttribute("rx");
|
||||||
@ -213,7 +214,7 @@ public class SvgImporter {
|
|||||||
drawPolygon(csd, c, polygon);
|
drawPolygon(csd, c, polygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawCircle(CustomShapeDescription csd, Element element, Context c) {
|
private void drawCircle(CustomShapeDescription.Builder csd, Element element, Context c) {
|
||||||
if (element.hasAttribute("id")) {
|
if (element.hasAttribute("id")) {
|
||||||
VectorInterface pos = c.v(c.getLength(element.getAttribute("cx")), c.getLength(element.getAttribute("cy")));
|
VectorInterface pos = c.v(c.getLength(element.getAttribute("cx")), c.getLength(element.getAttribute("cy")));
|
||||||
String id = element.getAttribute("id");
|
String id = element.getAttribute("id");
|
||||||
@ -279,7 +280,7 @@ public class SvgImporter {
|
|||||||
return new Vector(Math.round(pos.getXFloat() / SIZE) * SIZE, Math.round(pos.getYFloat() / SIZE) * SIZE);
|
return new Vector(Math.round(pos.getXFloat() / SIZE) * SIZE, Math.round(pos.getYFloat() / SIZE) * SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawText(CustomShapeDescription csd, Context c, Element element) throws SvgException {
|
private void drawText(CustomShapeDescription.Builder csd, Context c, Element element) throws SvgException {
|
||||||
VectorFloat p = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y")));
|
VectorFloat p = new VectorFloat(c.getLength(element.getAttribute("x")), c.getLength(element.getAttribute("y")));
|
||||||
VectorInterface pos0 = p.transform(c.getTransform());
|
VectorInterface pos0 = p.transform(c.getTransform());
|
||||||
VectorInterface pos1 = p.add(new VectorFloat(1, 0)).transform(c.getTransform());
|
VectorInterface pos1 = p.add(new VectorFloat(1, 0)).transform(c.getTransform());
|
||||||
@ -290,7 +291,7 @@ public class SvgImporter {
|
|||||||
drawTextElement(csd, c, element, pos0, pos1);
|
drawTextElement(csd, c, element, pos0, pos1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawTextElement(CustomShapeDescription csd, Context c, Element element, VectorInterface pos0, VectorInterface pos1) throws SvgException {
|
private void drawTextElement(CustomShapeDescription.Builder csd, Context c, Element element, VectorInterface pos0, VectorInterface pos1) throws SvgException {
|
||||||
NodeList nodes = element.getElementsByTagName("*");
|
NodeList nodes = element.getElementsByTagName("*");
|
||||||
if (nodes.getLength() == 0) {
|
if (nodes.getLength() == 0) {
|
||||||
String text = element.getTextContent();
|
String text = element.getTextContent();
|
||||||
|
@ -52,7 +52,7 @@ public class CustomShapeEditor extends EditorFactory.LabelEditor<CustomShapeDesc
|
|||||||
clear = new ToolTipAction(Lang.get("btn_clearData")) {
|
clear = new ToolTipAction(Lang.get("btn_clearData")) {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
customShapeDescription = CustomShapeDescription.EMPTY;
|
customShapeDescription = new CustomShapeDescription.Builder().build();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
panel.add(clear.createJButton());
|
panel.add(clear.createJButton());
|
||||||
|
@ -1010,12 +1010,12 @@ public final class EditorFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InverterConfig getInverterConfig() {
|
private InverterConfig getInverterConfig() {
|
||||||
InverterConfig ic = new InverterConfig();
|
InverterConfig.Builder ic = new InverterConfig.Builder();
|
||||||
for (JCheckBox cb : boxes) {
|
for (JCheckBox cb : boxes) {
|
||||||
if (cb.isSelected())
|
if (cb.isSelected())
|
||||||
ic.add(cb.getText());
|
ic.add(cb.getText());
|
||||||
}
|
}
|
||||||
return ic;
|
return ic.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ public class ROMEditorDialog extends JDialog {
|
|||||||
for (RomHolder rh : romlist)
|
for (RomHolder rh : romlist)
|
||||||
rm.addRom(rh.ri.getLabel(), rh.data);
|
rm.addRom(rh.ri.getLabel(), rh.data);
|
||||||
|
|
||||||
return rm.getMinimized();
|
return rm;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,12 +17,6 @@ import java.util.ArrayList;
|
|||||||
* The test data.
|
* The test data.
|
||||||
*/
|
*/
|
||||||
public class TestCaseDescription {
|
public class TestCaseDescription {
|
||||||
|
|
||||||
/**
|
|
||||||
* the default instance
|
|
||||||
*/
|
|
||||||
public static final TestCaseDescription DEFAULT = new TestCaseDescription("");
|
|
||||||
|
|
||||||
private String dataString;
|
private String dataString;
|
||||||
private transient LineEmitter lines;
|
private transient LineEmitter lines;
|
||||||
private transient ArrayList<String> names;
|
private transient ArrayList<String> names;
|
||||||
|
@ -18,7 +18,7 @@ public class TestCaseElement implements Element {
|
|||||||
/**
|
/**
|
||||||
* the used {@link ElementAttributes} key
|
* the used {@link ElementAttributes} key
|
||||||
*/
|
*/
|
||||||
public static final Key<TestCaseDescription> TESTDATA = new Key<>("Testdata", TestCaseDescription.DEFAULT);
|
public static final Key<TestCaseDescription> TESTDATA = new Key<>("Testdata", () -> new TestCaseDescription(""));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TestCaseElement description
|
* The TestCaseElement description
|
||||||
|
Loading…
x
Reference in New Issue
Block a user