/******************************************************************************* * 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.circuit; import java.awt.Graphics; import javax.swing.JPopupMenu; import com.bfh.logisim.designrulecheck.Netlist; import com.cburch.logisim.comp.ComponentDrawContext; import com.cburch.logisim.comp.ComponentEvent; import com.cburch.logisim.comp.ComponentFactory; import com.cburch.logisim.comp.ComponentUserEvent; import com.cburch.logisim.comp.EndData; import com.cburch.logisim.comp.ManagedComponent; import com.cburch.logisim.data.AttributeEvent; import com.cburch.logisim.data.AttributeListener; 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.Location; import com.cburch.logisim.instance.StdAttr; import com.cburch.logisim.proj.Project; import com.cburch.logisim.tools.MenuExtender; import com.cburch.logisim.tools.ToolTipMaker; import com.cburch.logisim.tools.WireRepair; import com.cburch.logisim.tools.WireRepairData; import com.cburch.logisim.util.GraphicsUtil; import com.cburch.logisim.util.StringUtil; public class Splitter extends ManagedComponent implements WireRepair, ToolTipMaker, MenuExtender, AttributeListener { private static void appendBuf(StringBuilder buf, int start, int end) { if (buf.length() > 0) buf.append(","); if (start == end) { buf.append(start); } else { buf.append(start + "-" + end); } } private boolean isMarked = false; public void SetMarked(boolean value) { isMarked = value; } public boolean isMarked() { return isMarked; } // basic data byte[] bit_thread; // how each bit maps to thread within end // derived data CircuitWires.SplitterData wire_data; public Splitter(Location loc, AttributeSet attrs) { super(loc, attrs, 3); configureComponent(); attrs.addAttributeListener(this); } // // AttributeListener methods // public void attributeListChanged(AttributeEvent e) { } public void attributeValueChanged(AttributeEvent e) { configureComponent(); } private synchronized void configureComponent() { SplitterAttributes attrs = (SplitterAttributes) getAttributeSet(); SplitterParameters parms = attrs.getParameters(); int fanout = attrs.fanout; byte[] bit_end = attrs.bit_end; // compute width of each end bit_thread = new byte[bit_end.length]; byte[] end_width = new byte[fanout + 1]; end_width[0] = (byte) bit_end.length; for (int i = 0; i < bit_end.length; i++) { byte thr = bit_end[i]; if (thr > 0) { bit_thread[i] = end_width[thr]; end_width[thr]++; } else { bit_thread[i] = -1; } } // compute end positions Location origin = getLocation(); int x = origin.getX() + parms.getEnd0X(); int y = origin.getY() + parms.getEnd0Y(); int dx = parms.getEndToEndDeltaX(); int dy = parms.getEndToEndDeltaY(); EndData[] ends = new EndData[fanout + 1]; ends[0] = new EndData(origin, BitWidth.create(bit_end.length), EndData.INPUT_OUTPUT); for (int i = 0; i < fanout; i++) { ends[i + 1] = new EndData(Location.create(x, y), BitWidth.create(end_width[i + 1]), EndData.INPUT_OUTPUT); x += dx; y += dy; } wire_data = new CircuitWires.SplitterData(fanout); setEnds(ends); recomputeBounds(); fireComponentInvalidated(new ComponentEvent(this)); } public void configureMenu(JPopupMenu menu, Project proj) { menu.addSeparator(); menu.add(new SplitterDistributeItem(proj, this, 1)); menu.add(new SplitterDistributeItem(proj, this, -1)); } @Override public boolean contains(Location loc) { if (super.contains(loc)) { Location myLoc = getLocation(); Direction facing = getAttributeSet().getValue(StdAttr.FACING); if (facing == Direction.EAST || facing == Direction.WEST) { return Math.abs(loc.getX() - myLoc.getX()) > 5 || loc.manhattanDistanceTo(myLoc) <= 5; } else { return Math.abs(loc.getY() - myLoc.getY()) > 5 || loc.manhattanDistanceTo(myLoc) <= 5; } } else { return false; } } // // user interface methods // public void draw(ComponentDrawContext context) { SplitterAttributes attrs = (SplitterAttributes) getAttributeSet(); if (attrs.appear == SplitterAttributes.APPEAR_LEGACY) { SplitterPainter.drawLegacy(context, attrs, getLocation()); } else { Location loc = getLocation(); SplitterPainter.drawLines(context, attrs, loc); SplitterPainter.drawLabels(context, attrs, loc); context.drawPins(this); } if (isMarked) { Graphics g = context.getGraphics(); Bounds bds = this.getBounds(); g.setColor(Netlist.DRC_INSTANCE_MARK_COLOR); GraphicsUtil.switchToWidth(g, 2); g.drawRoundRect(bds.getX()-10, bds.getY()-10, bds.getWidth()+20, bds.getHeight()+20, 20, 20); } } public byte[] GetEndpoints() { SplitterAttributes attrs = (SplitterAttributes) getAttributeSet(); return attrs.bit_end; } // // abstract ManagedComponent methods // @Override public ComponentFactory getFactory() { return SplitterFactory.instance; } @Override public Object getFeature(Object key) { if (key == WireRepair.class) return this; if (key == ToolTipMaker.class) return this; if (key == MenuExtender.class) return this; else return super.getFeature(key); } public String getToolTip(ComponentUserEvent e) { int end = -1; for (int i = getEnds().size() - 1; i >= 0; i--) { if (getEndLocation(i).manhattanDistanceTo(e.getX(), e.getY()) < 10) { end = i; break; } } if (end == 0) { return Strings.get("splitterCombinedTip"); } else if (end > 0) { int bits = 0; StringBuilder buf = new StringBuilder(); SplitterAttributes attrs = (SplitterAttributes) getAttributeSet(); byte[] bit_end = attrs.bit_end; boolean inString = false; int beginString = 0; for (int i = 0; i < bit_end.length; i++) { if (bit_end[i] == end) { bits++; if (!inString) { inString = true; beginString = i; } } else { if (inString) { appendBuf(buf, i - 1, beginString); inString = false; } } } if (inString) appendBuf(buf, bit_end.length - 1, beginString); String base; switch (bits) { case 0: base = Strings.get("splitterSplit0Tip"); break; case 1: base = Strings.get("splitterSplit1Tip"); break; default: base = Strings.get("splitterSplitManyTip"); break; } return StringUtil.format(base, buf.toString()); } else { return null; } } @Override public void propagate(CircuitState state) { ; // handled by CircuitWires, nothing to do } public boolean shouldRepairWire(WireRepairData data) { return true; } }