/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
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 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;
private boolean isInside(InstanceState state, MouseEvent e) {
Bounds bds = state.getInstance().getBounds();
return bds.contains(e.getX(), e.getY());
}
@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 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;
}
}
}
private static ClockState getState(InstanceState state) {
ClockState ret = (ClockState) state.getData();
if (ret == null) {
ret = new ClockState();
state.setData(ret);
}
return ret;
}
//
// 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;
}
}
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");
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);
}
//
// 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);
}
//
// 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
public String getHDLName(AttributeSet attrs) {
return "LogisimClockComponent";
}
@Override
public Bounds getOffsetBounds(AttributeSet attrs) {
return Probe.getOffsetBounds(attrs.getValue(StdAttr.FACING),
BitWidth.ONE, RadixOption.RADIX_2);
}
@Override
public boolean HDLSupportedComponent(String HDLIdentifier,
AttributeSet attrs) {
if (MyHDLGenerator == null) {
MyHDLGenerator = new ClockHDLGeneratorFactory();
}
return MyHDLGenerator.HDLTargetSupported(HDLIdentifier, attrs);
}
@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);
}
}
//
// 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();
}
@Override
public void propagate(InstanceState state) {
Value val = state.getPortValue(0);
ClockState q = getState(state);
if (!val.equals(q.sending)) { // ignore if no change
state.setPort(0, q.sending, 1);
}
}
}