/*******************************************************************************
* 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.FontMetrics;
import java.awt.Graphics;
import com.cburch.logisim.comp.TextField;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
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.util.GraphicsUtil;
public class Tunnel extends InstanceFactory {
public static final Tunnel FACTORY = new Tunnel();
static final int MARGIN = 3;
static final int ARROW_MARGIN = 5;
static final int ARROW_DEPTH = 4;
static final int ARROW_MIN_WIDTH = 16;
static final int ARROW_MAX_WIDTH = 20;
public Tunnel() {
super("Tunnel", Strings.getter("tunnelComponent"));
setIconName("tunnel.gif");
setFacingAttribute(StdAttr.FACING);
setKeyConfigurator(new BitWidthConfigurator(StdAttr.WIDTH));
}
private Bounds computeBounds(TunnelAttributes attrs, int textWidth,
int textHeight, Graphics g, String label) {
int x = attrs.getLabelX();
int y = attrs.getLabelY();
int halign = attrs.getLabelHAlign();
int valign = attrs.getLabelVAlign();
int minDim = ARROW_MIN_WIDTH - 2 * MARGIN;
int bw = Math.max(minDim, textWidth);
int bh = Math.max(minDim, textHeight);
int bx;
int by;
switch (halign) {
case TextField.H_LEFT:
bx = x;
break;
case TextField.H_RIGHT:
bx = x - bw;
break;
default:
bx = x - (bw / 2);
}
switch (valign) {
case TextField.V_TOP:
by = y;
break;
case TextField.V_BOTTOM:
by = y - bh;
break;
default:
by = y - (bh / 2);
}
if (g != null) {
GraphicsUtil.drawText(g, label, bx + bw / 2, by + bh / 2,
GraphicsUtil.H_CENTER, GraphicsUtil.V_CENTER_OVERALL);
}
return Bounds.create(bx, by, bw, bh).expand(MARGIN).add(0, 0);
}
//
// private methods
//
private void configureLabel(Instance instance) {
TunnelAttributes attrs = (TunnelAttributes) instance.getAttributeSet();
Location loc = instance.getLocation();
instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT, loc.getX()
+ attrs.getLabelX(), loc.getY() + attrs.getLabelY(),
attrs.getLabelHAlign(), attrs.getLabelVAlign());
}
//
// methods for instances
//
@Override
protected void configureNewInstance(Instance instance) {
instance.addAttributeListener();
instance.setPorts(new Port[] { new Port(0, 0, Port.INOUT, StdAttr.WIDTH) });
configureLabel(instance);
}
@Override
public AttributeSet createAttributeSet() {
return new TunnelAttributes();
}
@Override
public Bounds getOffsetBounds(AttributeSet attrsBase) {
TunnelAttributes attrs = (TunnelAttributes) attrsBase;
Bounds bds = attrs.getOffsetBounds();
if (bds != null) {
return bds;
} else {
int ht = attrs.getFont().getSize();
int wd = ht * attrs.getLabel().length() / 2;
bds = computeBounds(attrs, wd, ht, null, "");
attrs.setOffsetBounds(bds);
return bds;
}
}
@Override
public boolean HDLSupportedComponent(String HDLIdentifier,
AttributeSet attrs) {
return true;
}
@Override
protected void instanceAttributeChanged(Instance instance, Attribute<?> attr) {
if (attr == StdAttr.FACING) {
configureLabel(instance);
instance.recomputeBounds();
} else if (attr == StdAttr.LABEL || attr == StdAttr.LABEL_FONT) {
instance.recomputeBounds();
}
}
//
// graphics methods
//
@Override
public void paintGhost(InstancePainter painter) {
TunnelAttributes attrs = (TunnelAttributes) painter.getAttributeSet();
Direction facing = attrs.getFacing();
String label = attrs.getLabel();
Graphics g = painter.getGraphics();
g.setFont(attrs.getFont());
FontMetrics fm = g.getFontMetrics();
Bounds bds = computeBounds(attrs, fm.stringWidth(label), fm.getAscent()
+ fm.getDescent(), g, label);
if (attrs.setOffsetBounds(bds)) {
Instance instance = painter.getInstance();
if (instance != null)
instance.recomputeBounds();
}
int x0 = bds.getX();
int y0 = bds.getY();
int x1 = x0 + bds.getWidth();
int y1 = y0 + bds.getHeight();
int mw = ARROW_MAX_WIDTH / 2;
int[] xp;
int[] yp;
if (facing == Direction.NORTH) {
int yb = y0 + ARROW_DEPTH;
if (x1 - x0 <= ARROW_MAX_WIDTH) {
xp = new int[] { x0, 0, x1, x1, x0 };
yp = new int[] { yb, y0, yb, y1, y1 };
} else {
xp = new int[] { x0, -mw, 0, mw, x1, x1, x0 };
yp = new int[] { yb, yb, y0, yb, yb, y1, y1 };
}
} else if (facing == Direction.SOUTH) {
int yb = y1 - ARROW_DEPTH;
if (x1 - x0 <= ARROW_MAX_WIDTH) {
xp = new int[] { x0, x1, x1, 0, x0 };
yp = new int[] { y0, y0, yb, y1, yb };
} else {
xp = new int[] { x0, x1, x1, mw, 0, -mw, x0 };
yp = new int[] { y0, y0, yb, yb, y1, yb, yb };
}
} else if (facing == Direction.EAST) {
int xb = x1 - ARROW_DEPTH;
if (y1 - y0 <= ARROW_MAX_WIDTH) {
xp = new int[] { x0, xb, x1, xb, x0 };
yp = new int[] { y0, y0, 0, y1, y1 };
} else {
xp = new int[] { x0, xb, xb, x1, xb, xb, x0 };
yp = new int[] { y0, y0, -mw, 0, mw, y1, y1 };
}
} else {
int xb = x0 + ARROW_DEPTH;
if (y1 - y0 <= ARROW_MAX_WIDTH) {
xp = new int[] { xb, x1, x1, xb, x0 };
yp = new int[] { y0, y0, y1, y1, 0 };
} else {
xp = new int[] { xb, x1, x1, xb, xb, x0, xb };
yp = new int[] { y0, y0, y1, y1, mw, 0, -mw };
}
}
GraphicsUtil.switchToWidth(g, 2);
g.drawPolygon(xp, yp, xp.length);
}
@Override
public void paintInstance(InstancePainter painter) {
Location loc = painter.getLocation();
int x = loc.getX();
int y = loc.getY();
Graphics g = painter.getGraphics();
g.translate(x, y);
g.setColor(Color.BLACK);
paintGhost(painter);
g.translate(-x, -y);
painter.drawPorts();
}
@Override
public void propagate(InstanceState state) {
; // nothing to do - handled by circuit
}
}