/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.std.wiring;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.cburch.logisim.analyze.model.Expression;
import com.cburch.logisim.analyze.model.Expressions;
import com.cburch.logisim.circuit.ExpressionComputer;
import com.cburch.logisim.data.AbstractAttributeSet;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Attributes;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceFactory;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.tools.key.BitWidthConfigurator;
import com.cburch.logisim.tools.key.JoinedConfigurator;
import com.cburch.logisim.util.GraphicsUtil;
import static com.cburch.logisim.util.LocaleString.*;
public class Constant extends InstanceFactory {
public static final Attribute<Integer> ATTR_VALUE
= Attributes.forHexInteger("value", getFromLocale("constantValueAttr"));
public static InstanceFactory FACTORY = new Constant();
private static final Color BACKGROUND_COLOR = new Color(230, 230, 230);
private static final List<Attribute<?>> ATTRIBUTES
= Arrays.asList(new Attribute<?>[] {
StdAttr.FACING, StdAttr.WIDTH, ATTR_VALUE
});
private static class ConstantAttributes extends AbstractAttributeSet {
private Direction facing = Direction.EAST;;
private BitWidth width = BitWidth.ONE;
private Value value = Value.TRUE;
@Override
protected void copyInto(AbstractAttributeSet destObj) {
ConstantAttributes dest = (ConstantAttributes) destObj;
dest.facing = this.facing;
dest.width = this.width;
dest.value = this.value;
}
@Override
public List<Attribute<?>> getAttributes() {
return ATTRIBUTES;
}
@Override
@SuppressWarnings("unchecked")
public <V> V getValue(Attribute<V> attr) {
if (attr == StdAttr.FACING) return (V) facing;
if (attr == StdAttr.WIDTH) return (V) width;
if (attr == ATTR_VALUE) return (V) Integer.valueOf(value.toIntValue());
return null;
}
@Override
public <V> void setValue(Attribute<V> attr, V value) {
if (attr == StdAttr.FACING) {
facing = (Direction) value;
} else if (attr == StdAttr.WIDTH) {
width = (BitWidth) value;
this.value = this.value.extendWidth(width.getWidth(),
this.value.get(this.value.getWidth() - 1));
} else if (attr == ATTR_VALUE) {
int val = ((Integer) value).intValue();
this.value = Value.createKnown(width, val);
} else {
throw new IllegalArgumentException("unknown attribute " + attr);
}
fireAttributeValueChanged(attr, value);
}
}
private static class ConstantExpression implements ExpressionComputer {
private Instance instance;
public ConstantExpression(Instance instance) {
this.instance = instance;
}
@Override
public void computeExpression(Map<Location,Expression> expressionMap) {
AttributeSet attrs = instance.getAttributeSet();
int intValue = attrs.getValue(ATTR_VALUE).intValue();
expressionMap.put(instance.getLocation(),
Expressions.constant(intValue));
}
}
public Constant() {
super("Constant", getFromLocale("constantComponent"));
setFacingAttribute(StdAttr.FACING);
setKeyConfigurator(JoinedConfigurator.create(
new ConstantConfigurator(),
new BitWidthConfigurator(StdAttr.WIDTH)));
}
@Override
public AttributeSet createAttributeSet() {
return new ConstantAttributes();
}
@Override
protected void configureNewInstance(Instance instance) {
instance.addAttributeListener();
updatePorts(instance);
}
private void updatePorts(Instance instance) {
Port[] ps = { new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH) };
instance.setPorts(ps);
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == StdAttr.WIDTH) {
instance.recomputeBounds();
updatePorts(instance);
} else if (attr == StdAttr.FACING) {
instance.recomputeBounds();
} else if (attr == ATTR_VALUE) {
instance.fireInvalidated();
}
}
@Override
protected Object getInstanceFeature(Instance instance, Object key) {
if (key == ExpressionComputer.class) return new ConstantExpression(instance);
return super.getInstanceFeature(instance, key);
}
@Override
public void propagate(InstanceState state) {
BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
int value = state.getAttributeValue(ATTR_VALUE).intValue();
state.setPort(0, Value.createKnown(width, value), 1);
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
Direction facing = attrs.getValue(StdAttr.FACING);
BitWidth width = attrs.getValue(StdAttr.WIDTH);
int chars = (width.getWidth() + 3) / 4;
Bounds ret = null;
if (facing == Direction.EAST) {
switch (chars) {
case 1: ret = Bounds.create(-16, -8, 16, 16); break;
case 2: ret = Bounds.create(-16, -8, 16, 16); break;
case 3: ret = Bounds.create(-26, -8, 26, 16); break;
case 4: ret = Bounds.create(-36, -8, 36, 16); break;
case 5: ret = Bounds.create(-46, -8, 46, 16); break;
case 6: ret = Bounds.create(-56, -8, 56, 16); break;
case 7: ret = Bounds.create(-66, -8, 66, 16); break;
case 8: ret = Bounds.create(-76, -8, 76, 16); break;
}
} else if (facing == Direction.WEST) {
switch (chars) {
case 1: ret = Bounds.create( 0, -8, 16, 16); break;
case 2: ret = Bounds.create( 0, -8, 16, 16); break;
case 3: ret = Bounds.create( 0, -8, 26, 16); break;
case 4: ret = Bounds.create( 0, -8, 36, 16); break;
case 5: ret = Bounds.create( 0, -8, 46, 16); break;
case 6: ret = Bounds.create( 0, -8, 56, 16); break;
case 7: ret = Bounds.create( 0, -8, 66, 16); break;
case 8: ret = Bounds.create( 0, -8, 76, 16); break;
}
} else if (facing == Direction.SOUTH) {
switch (chars) {
case 1: ret = Bounds.create(-8, -16, 16, 16); break;
case 2: ret = Bounds.create(-8, -16, 16, 16); break;
case 3: ret = Bounds.create(-13, -16, 26, 16); break;
case 4: ret = Bounds.create(-18, -16, 36, 16); break;
case 5: ret = Bounds.create(-23, -16, 46, 16); break;
case 6: ret = Bounds.create(-28, -16, 56, 16); break;
case 7: ret = Bounds.create(-33, -16, 66, 16); break;
case 8: ret = Bounds.create(-38, -16, 76, 16); break;
}
} else if (facing == Direction.NORTH) {
switch (chars) {
case 1: ret = Bounds.create(-8, 0, 16, 16); break;
case 2: ret = Bounds.create(-8, 0, 16, 16); break;
case 3: ret = Bounds.create(-13, 0, 26, 16); break;
case 4: ret = Bounds.create(-18, 0, 36, 16); break;
case 5: ret = Bounds.create(-23, 0, 46, 16); break;
case 6: ret = Bounds.create(-28, 0, 56, 16); break;
case 7: ret = Bounds.create(-33, 0, 66, 16); break;
case 8: ret = Bounds.create(-38, 0, 76, 16); break;
}
}
if (ret == null) {
throw new IllegalArgumentException("unrecognized arguments " + facing + " " + width);
}
return ret;
}
//
// painting methods
//
@Override
public void paintIcon(InstancePainter painter) {
int w = painter.getAttributeValue(StdAttr.WIDTH).getWidth();
int pinx = 16; int piny = 9;
Direction dir = painter.getAttributeValue(StdAttr.FACING);
// keep defaults
if (dir == Direction.EAST) { }
else if (dir == Direction.WEST) { pinx = 4; }
else if (dir == Direction.NORTH) { pinx = 9; piny = 4; }
else if (dir == Direction.SOUTH) { pinx = 9; piny = 16; }
Graphics g = painter.getGraphics();
if (w == 1) {
int v = painter.getAttributeValue(ATTR_VALUE).intValue();
Value val = v == 1 ? Value.TRUE : Value.FALSE;
g.setColor(val.getColor());
GraphicsUtil.drawCenteredText(g, "" + v, 10, 9);
} else {
g.setFont(g.getFont().deriveFont(9.0f));
GraphicsUtil.drawCenteredText(g, "x" + w, 10, 9);
}
g.fillOval(pinx, piny, 3, 3);
}
@Override
public void paintGhost(InstancePainter painter) {
int v = painter.getAttributeValue(ATTR_VALUE).intValue();
String vStr = Integer.toHexString(v);
Bounds bds = getOffsetBounds(painter.getAttributeSet());
Graphics g = painter.getGraphics();
GraphicsUtil.switchToWidth(g, 2);
g.fillOval(-2, -2, 5, 5);
GraphicsUtil.drawCenteredText(g, vStr, bds.getX() + bds.getWidth() / 2,
bds.getY() + bds.getHeight() / 2);
}
@Override
public void paintInstance(InstancePainter painter) {
Bounds bds = painter.getOffsetBounds();
BitWidth width = painter.getAttributeValue(StdAttr.WIDTH);
int intValue = painter.getAttributeValue(ATTR_VALUE).intValue();
Value v = Value.createKnown(width, intValue);
Location loc = painter.getLocation();
int x = loc.getX();
int y = loc.getY();
Graphics g = painter.getGraphics();
if (painter.shouldDrawColor()) {
g.setColor(BACKGROUND_COLOR);
g.fillRect(x + bds.getX(), y + bds.getY(), bds.getWidth(), bds.getHeight());
}
if (v.getWidth() == 1) {
if (painter.shouldDrawColor()) g.setColor(v.getColor());
GraphicsUtil.drawCenteredText(g, v.toString(),
x + bds.getX() + bds.getWidth() / 2,
y + bds.getY() + bds.getHeight() / 2 - 2);
} else {
g.setColor(Color.BLACK);
GraphicsUtil.drawCenteredText(g, v.toHexString(),
x + bds.getX() + bds.getWidth() / 2,
y + bds.getY() + bds.getHeight() / 2 - 2);
}
painter.drawPorts();
}
//TODO: Allow editing of value via text tool/attribute table
}