/* 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.awt.event.MouseEvent;
import javax.swing.Icon;
import com.cburch.logisim.circuit.CircuitState;
import com.cburch.logisim.circuit.RadixOption;
import com.cburch.logisim.comp.Component;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceData;
import com.cburch.logisim.instance.InstanceFactory;
import com.cburch.logisim.instance.InstanceLogger;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstancePoker;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.util.GraphicsUtil;
import com.cburch.logisim.util.Icons;
public class Clock extends InstanceFactory {
public static final Attribute<Integer> ATTR_HIGH
= new DurationAttribute("highDuration", Strings.getter("clockHighAttr"),
1, Integer.MAX_VALUE);
public static final Attribute<Integer> ATTR_LOW
= new DurationAttribute("lowDuration", Strings.getter("clockLowAttr"),
1, Integer.MAX_VALUE);
public static final Clock FACTORY = new Clock();
private static final Icon toolIcon = Icons.getIcon("clock.gif");
private static class ClockState implements InstanceData, Cloneable {
Value sending = Value.FALSE;
int clicks = 0;
@Override
public ClockState clone() {
try { return (ClockState) super.clone(); }
catch (CloneNotSupportedException e) { return null; }
}
}
public static class ClockLogger extends InstanceLogger {
@Override
public String getLogName(InstanceState state, Object option) {
return state.getAttributeValue(StdAttr.LABEL);
}
@Override
public Value getLogValue(InstanceState state, Object option) {
ClockState s = getState(state);
return s.sending;
}
}
public static class ClockPoker extends InstancePoker {
boolean isPressed = true;
@Override
public void mousePressed(InstanceState state, MouseEvent e) {
isPressed = isInside(state, e);
}
@Override
public void mouseReleased(InstanceState state, MouseEvent e) {
if (isPressed && isInside(state, e)) {
ClockState myState = (ClockState) state.getData();
myState.sending = myState.sending.not();
myState.clicks++;
state.fireInvalidated();
}
isPressed = false;
}
private boolean isInside(InstanceState state, MouseEvent e) {
Bounds bds = state.getInstance().getBounds();
return bds.contains(e.getX(), e.getY());
}
}
public Clock() {
super("Clock", Strings.getter("clockComponent"));
setAttributes(new Attribute[] {
StdAttr.FACING, ATTR_HIGH, ATTR_LOW,
StdAttr.LABEL, Pin.ATTR_LABEL_LOC, StdAttr.LABEL_FONT
}, new Object[] {
Direction.EAST, Integer.valueOf(1), Integer.valueOf(1),
"", Direction.WEST, StdAttr.DEFAULT_LABEL_FONT
});
setFacingAttribute(StdAttr.FACING);
setInstanceLogger(ClockLogger.class);
setInstancePoker(ClockPoker.class);
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
return Probe.getOffsetBounds(
attrs.getValue(StdAttr.FACING),
BitWidth.ONE, RadixOption.RADIX_2);
}
//
// graphics methods
//
@Override
public void paintIcon(InstancePainter painter) {
Graphics g = painter.getGraphics();
if (toolIcon != null) {
toolIcon.paintIcon(painter.getDestination(), g, 2, 2);
} else {
g.drawRect(4, 4, 13, 13);
g.setColor(Value.FALSE.getColor());
g.drawPolyline(new int[] { 6, 6, 10, 10, 14, 14 },
new int[] { 10, 6, 6, 14, 14, 10 }, 6);
}
Direction dir = painter.getAttributeValue(StdAttr.FACING);
int pinx = 15; int piny = 8;
if (dir == Direction.EAST) { // keep defaults
} else if (dir == Direction.WEST) { pinx = 3;
} else if (dir == Direction.NORTH) { pinx = 8; piny = 3;
} else if (dir == Direction.SOUTH) { pinx = 8; piny = 15;
}
g.setColor(Value.TRUE.getColor());
g.fillOval(pinx, piny, 3, 3);
}
@Override
public void paintInstance(InstancePainter painter) {
java.awt.Graphics g = painter.getGraphics();
Bounds bds = painter.getInstance().getBounds(); // intentionally with no graphics object - we don't want label included
int x = bds.getX();
int y = bds.getY();
GraphicsUtil.switchToWidth(g, 2);
g.setColor(Color.BLACK);
g.drawRect(x, y, bds.getWidth(), bds.getHeight());
painter.drawLabel();
boolean drawUp;
if (painter.getShowState()) {
ClockState state = getState(painter);
g.setColor(state.sending.getColor());
drawUp = state.sending == Value.TRUE;
} else {
g.setColor(Color.BLACK);
drawUp = true;
}
x += 10;
y += 10;
int[] xs = { x - 6, x - 6, x, x, x + 6, x + 6 };
int[] ys;
if (drawUp) {
ys = new int[] { y, y - 4, y - 4, y + 4, y + 4, y };
} else {
ys = new int[] { y, y + 4, y + 4, y - 4, y - 4, y };
}
g.drawPolyline(xs, ys, xs.length);
painter.drawPorts();
}
//
// methods for instances
//
@Override
protected void configureNewInstance(Instance instance) {
instance.addAttributeListener();
instance.setPorts(new Port[] { new Port(0, 0, Port.OUTPUT, BitWidth.ONE) });
configureLabel(instance);
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == Pin.ATTR_LABEL_LOC) {
configureLabel(instance);
} else if (attr == StdAttr.FACING) {
instance.recomputeBounds();
configureLabel(instance);
}
}
@Override
public void propagate(InstanceState state) {
Value val = state.getPort(0);
ClockState q = getState(state);
if (!val.equals(q.sending)) { // ignore if no change
state.setPort(0, q.sending, 1);
}
}
//
// package methods
//
public static boolean tick(CircuitState circState, int ticks, Component comp) {
AttributeSet attrs = comp.getAttributeSet();
int durationHigh = attrs.getValue(ATTR_HIGH).intValue();
int durationLow = attrs.getValue(ATTR_LOW).intValue();
ClockState state = (ClockState) circState.getData(comp);
if (state == null) {
state = new ClockState();
circState.setData(comp, state);
}
boolean curValue = ticks % (durationHigh + durationLow) < durationLow;
if (state.clicks % 2 == 1) curValue = !curValue;
Value desired = (curValue ? Value.FALSE : Value.TRUE);
if (!state.sending.equals(desired)) {
state.sending = desired;
Instance.getInstanceFor(comp).fireInvalidated();
return true;
} else {
return false;
}
}
//
// private methods
//
private void configureLabel(Instance instance) {
Direction facing = instance.getAttributeValue(StdAttr.FACING);
Direction labelLoc = instance.getAttributeValue(Pin.ATTR_LABEL_LOC);
Probe.configureLabel(instance, labelLoc, facing);
}
private static ClockState getState(InstanceState state) {
ClockState ret = (ClockState) state.getData();
if (ret == null) {
ret = new ClockState();
state.setData(ret);
}
return ret;
}
}