/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ExportChanges.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) 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.
*
* Electric(tm) 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 Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.user;
import com.sun.electric.Main;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.network.NetworkTool;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.DisplayedText;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.user.menus.MenuCommands;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import java.awt.Font;
import java.awt.Point;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JOptionPane;
/**
* This class has all of the Export change commands in Electric.
*/
public final class ExportChanges
{
/****************************** EXPORT LISTING ******************************/
public static void describeExports(boolean summarize)
{
new DescribeExports(summarize);
}
private static class ExportList
{
Export pp;
int equiv;
int busList;
}
/**
* Class to rename an export in a new thread.
*/
private static class DescribeExports extends Job
{
private Cell cell;
private boolean summarize;
protected DescribeExports(boolean summarize)
{
super("Describe Exports", User.getUserTool(), Job.Type.SERVER_EXAMINE, null, null, Job.Priority.USER);
cell = WindowFrame.needCurCell();
this.summarize = summarize;
startJob();
}
public boolean doIt() throws JobException
{
if (cell == null) return false;
Netlist netlist = cell.getNetlist();
if (netlist == null)
{
System.out.println("Sorry, a deadlock aborted your query (network information unavailable). Please try again");
return false;
}
// compute the associated cell to check
Cell wnp = cell.contentsView();
if (wnp == null) wnp = cell.iconView();
if (wnp == cell) wnp = null;
// count the number of exports
if (cell.getNumPorts() == 0)
{
System.out.println("There are no exports on " + cell);
return true;
}
// make a list of exports
List<ExportList> exports = new ArrayList<ExportList>();
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
ExportList el = new ExportList();
el.pp = (Export)it.next();
el.equiv = -1;
el.busList = -1;
exports.add(el);
}
// sort exports by name within type
Collections.sort(exports, new ExportSortedByNameAndType());
// if summarizing, make associations that combine exports
int num_found = exports.size();
if (summarize)
{
// make associations among electrically equivalent exports
for(int j=0; j<num_found; j++)
{
int eqJ = exports.get(j).equiv;
int blJ = exports.get(j).busList;
if (eqJ != -1 || blJ != -1) continue;
Export ppJ = exports.get(j).pp;
for(int k=j+1; k<num_found; k++)
{
int eqK = exports.get(k).equiv;
int blK = exports.get(k).busList;
if (eqK != -1 || blK != -1) continue;
Export ppK = exports.get(k).pp;
if (ppJ.getCharacteristic() != ppK.getCharacteristic()) break;
if (!netlist.sameNetwork(ppJ.getOriginalPort().getNodeInst(), ppJ.getOriginalPort().getPortProto(),
ppK.getOriginalPort().getNodeInst(), ppK.getOriginalPort().getPortProto())) continue;
exports.get(k).equiv = j;
exports.get(j).equiv = -2;
}
}
// make associations among bussed exports
for(int j=0; j<num_found; j++)
{
int eqJ = exports.get(j).equiv;
int blJ = exports.get(j).busList;
if (eqJ != -1 || blJ != -1) continue;
Export ppJ = exports.get(j).pp;
String ptJ = ppJ.getName();
int sqPosJ = ptJ.indexOf('[');
if (sqPosJ < 0) continue;
for(int k=j+1; k<num_found; k++)
{
int eqK = exports.get(k).equiv;
int blK = exports.get(k).busList;
if (eqK != -1 || blK != -1) continue;
Export ppK = exports.get(k).pp;
if (ppJ.getCharacteristic() != ppK.getCharacteristic()) break;
String ptK = ppK.getName();
int sqPosK = ptK.indexOf('[');
if (sqPosJ != sqPosK) continue;
if (ptJ.substring(0, sqPosJ).equalsIgnoreCase(ptK.substring(0, sqPosK)))
{
exports.get(k).busList = j;
exports.get(j).busList = -2;
}
}
}
}
// describe each export
System.out.println("----- Exports on " + cell + ": total " + num_found + " -----");
Set<ArcProto> arcsSeen = new HashSet<ArcProto>();
for(int j=0; j<num_found; j++)
{
ExportList el = exports.get(j);
Export pp = el.pp;
if (el.equiv >= 0 || el.busList >= 0) continue;
// reset flags for arcs that can connect
for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
{
Technology tech = it.next();
for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
arcsSeen.remove(ap);
}
}
String infstr = "";
String activity = pp.getCharacteristic().getFullName();
int m = j+1;
for( ; m<num_found; m++)
{
if (exports.get(m).equiv == j) break;
}
double lx = 0, hx = 0, ly = 0, hy = 0;
if (m < num_found)
{
// many exports that are electrically equivalent
infstr += activity + " exports ";
for(int k=j; k<num_found; k++)
{
if (j != k && exports.get(k).equiv != j) continue;
if (j != k) infstr += ", ";
Export opp = exports.get(k).pp;
infstr += "'" + opp.getName() + "'";
Poly poly = opp.getPoly();
double x = poly.getCenterX();
double y = poly.getCenterY();
if (j == k)
{
lx = hx = x; ly = hy = y;
} else
{
if (x < lx) lx = x;
if (x > hx) hx = x;
if (y < ly) ly = y;
if (y > hy) hy = y;
}
ArcProto [] arcList = opp.getBasePort().getConnections();
for(int a=0; a<arcList.length; a++)
arcsSeen.add(arcList[a]);
}
infstr += " at (" + lx + "<=X<=" + hx + ", " + ly + "<=Y<=" + hy + "), electrically connected to";
infstr = addPossibleArcConnections(infstr, arcsSeen);
} else
{
m = j + 1;
for( ; m<num_found; m++)
{
if (exports.get(m).busList == j) break;
}
if (m < num_found)
{
// many exports from the same bus
int tot = 0;
for(int k=j; k<num_found; k++)
{
if (j != k && exports.get(k).busList != j) continue;
tot++;
Export opp = exports.get(k).pp;
Poly poly = opp.getPoly();
double x = poly.getCenterX();
double y = poly.getCenterY();
if (j == k)
{
lx = hx = x; ly = hy = y;
} else
{
if (x < lx) lx = x;
if (x > hx) hx = x;
if (y < ly) ly = y;
if (y > hy) hy = y;
}
ArcProto [] arcList = opp.getBasePort().getConnections();
for(int a=0; a<arcList.length; a++)
arcsSeen.add(arcList[a]);
}
List<Export> sortedBusList = new ArrayList<Export>();
sortedBusList.add(exports.get(j).pp);
for(int k=j+1; k<num_found; k++)
{
ExportList elK = exports.get(k);
if (elK.busList == j) sortedBusList.add(elK.pp);
}
// sort the bus by indices
Collections.sort(sortedBusList, new ExportSortedByBusIndex());
boolean first = true;
for(Export ppS : sortedBusList)
{
String pt1 = ppS.getName();
int openPos = pt1.indexOf('[');
if (first)
{
infstr += activity + " ports '" + pt1.substring(0, openPos) + "[";
first = false;
} else
{
infstr += ",";
}
int closePos = pt1.lastIndexOf(']');
infstr += pt1.substring(openPos+1, closePos);
}
infstr += "]' at (" + lx + "<=X<=" + hx + ", " + ly + "<=Y<=" + hy + "), same bus, connects to";
infstr = addPossibleArcConnections(infstr, arcsSeen);
} else
{
// isolated export
Poly poly = pp.getPoly();
double x = poly.getCenterX();
double y = poly.getCenterY();
infstr += activity + " export '" + pp.getName() + "' at (" + x + ", " + y + ") connects to";
ArcProto [] arcList = pp.getBasePort().getConnections();
for(int a=0; a<arcList.length; a++)
arcsSeen.add(arcList[a]);
infstr = addPossibleArcConnections(infstr, arcsSeen);
// check for the export in the associated cell
if (wnp != null)
{
if (pp.findEquivalent(wnp) == null)
infstr += " *** no equivalent in " + wnp;
}
}
}
TextUtils.printLongString(infstr);
}
if (wnp != null)
{
for(Iterator<PortProto> it = wnp.getPorts(); it.hasNext(); )
{
Export pp = (Export)it.next();
if (pp.findEquivalent(cell) == null)
System.out.println("*** Export " + pp.getName() + ", found in " + wnp + ", is missing here");
}
}
return true;
}
}
/**
* Helper method to add all marked arc prototypes to the infinite string.
* Marking is done by having the "temp1" field be nonzero.
*/
private static String addPossibleArcConnections(String infstr, Set<ArcProto> arcsSeen)
{
int i = 0;
for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
{
Technology tech = it.next();
for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
if (!arcsSeen.contains(ap)) i++;
}
}
if (i == 0) infstr += " EVERYTHING"; else
{
i = 0;
for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
{
Technology tech = it.next();
if (tech == Generic.tech()) continue;
for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
if (!arcsSeen.contains(ap)) continue;
if (i != 0) infstr += ",";
i++;
infstr += " " + ap.getName();
}
}
}
return infstr;
}
private static class ExportSortedByNameAndType implements Comparator<ExportList>
{
public int compare(ExportList el1, ExportList el2)
{
Export e1 = el1.pp;
Export e2 = el2.pp;
PortCharacteristic ch1 = e1.getCharacteristic();
PortCharacteristic ch2 = e2.getCharacteristic();
if (ch1 != ch2) return ch1.getOrder() - ch2.getOrder();
String s1 = e1.getName();
String s2 = e2.getName();
return TextUtils.STRING_NUMBER_ORDER.compare(s1, s2);
}
}
public static class ExportSortedByBusIndex implements Comparator<Export>
{
public int compare(Export e1, Export e2)
{
String s1 = e1.getName();
String s2 = e2.getName();
return TextUtils.STRING_NUMBER_ORDER.compare(s1, s2);
}
}
private static class PortInstsSortedByBusIndex implements Comparator<PortInst>
{
public int compare(PortInst p1, PortInst p2)
{
String s1 = p1.getPortProto().getName();
String s2 = p2.getPortProto().getName();
return TextUtils.STRING_NUMBER_ORDER.compare(s1, s2);
}
}
/**
* Class to follow the current export up the hierarchy.
* Lists all networks and exports connected in higher cells.
*/
public static class FollowExport extends Job
{
private Cell cell;
public FollowExport()
{
super("Re-export highlighted", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
// make sure there is a current cell
cell = WindowFrame.needCurCell();
if (cell == null) return;
startJob();
}
public boolean doIt() throws JobException
{
List<Export> exportsToFollow = getSelectedExports();
if (exportsToFollow.size() == 0)
{
System.out.println("There are no selected exports to follow");
return false;
}
Map<Cell,Set<Network>> networksSeen = new HashMap<Cell,Set<Network>>();
Map<Cell,Set<Export>> exportsSeen = new HashMap<Cell,Set<Export>>();
List<Export> exportsFollowed = new ArrayList<Export>();
for(Export e : exportsToFollow) exportsFollowed.add(e);
for(int i=0; i<exportsFollowed.size(); i++)
{
Export e = exportsFollowed.get(i);
Cell upperCell = e.getParent();
for(Iterator<NodeInst> nIt = upperCell.getInstancesOf(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
Cell higher = ni.getParent();
Set<Network> netsSeenInCell = networksSeen.get(higher);
if (netsSeenInCell == null)
{
netsSeenInCell = new HashSet<Network>();
networksSeen.put(higher, netsSeenInCell);
}
Netlist nl = higher.getNetlist();
Network net = nl.getNetwork(ni, e, 0);
if (net == null) continue;
if (netsSeenInCell.contains(net)) continue;
netsSeenInCell.add(net);
for(Iterator<Export> it = net.getExports(); it.hasNext(); )
{
Export furtherUp = it.next();
Set<Export> exportsSeenInCell = exportsSeen.get(higher);
if (exportsSeenInCell == null)
{
exportsSeenInCell = new HashSet<Export>();
exportsSeen.put(higher, exportsSeenInCell);
}
if (exportsSeenInCell.contains(furtherUp)) continue;
exportsSeenInCell.add(furtherUp);
exportsFollowed.add(furtherUp);
}
}
// now consider icon cells
Cell iconCell = upperCell.iconView();
if (iconCell != null)
{
for(Iterator<NodeInst> nIt = iconCell.getInstancesOf(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.isIconOfParent()) continue;
Cell higher = ni.getParent();
Set<Network> netsSeenInCell = networksSeen.get(higher);
if (netsSeenInCell == null)
{
netsSeenInCell = new HashSet<Network>();
networksSeen.put(higher, netsSeenInCell);
}
Netlist nl = higher.getNetlist();
Network net = nl.getNetwork(ni, e, 0);
if (netsSeenInCell.contains(net)) continue;
netsSeenInCell.add(net);
for(Iterator<Export> it = net.getExports(); it.hasNext(); )
{
Export furtherUp = it.next();
Set<Export> exportsSeenInCell = exportsSeen.get(higher);
if (exportsSeenInCell == null)
{
exportsSeenInCell = new HashSet<Export>();
exportsSeen.put(higher, exportsSeenInCell);
}
if (exportsSeenInCell.contains(furtherUp)) continue;
exportsSeenInCell.add(furtherUp);
exportsFollowed.add(furtherUp);
}
}
}
}
if (networksSeen.size() == 0)
{
System.out.println("The selected Exports are not used anywhere");
return true;
}
if (exportsToFollow.size() > 1)
{
System.out.print("The Exports ");
for(int i=0; i<exportsToFollow.size(); i++)
{
if (i > 0) System.out.print(",");
System.out.print(" " + exportsToFollow.get(i).getName());
}
System.out.println(" are used:");
} else System.out.println("The Export " + exportsToFollow.get(0).getName() + " is used:");
for(Cell c : networksSeen.keySet())
{
Set<Network> netsSeenInCell = networksSeen.get(c);
Set<Export> exportsSeenInCell = exportsSeen.get(c);
System.out.print(" Cell " + c.describe(false));
if (netsSeenInCell.size() > 1) System.out.print(" networks"); else
System.out.print(" network");
boolean comma = false;
for(Network n : netsSeenInCell)
{
if (comma) System.out.print(",");
comma = true;
System.out.print(" " + n.getName());
}
System.out.println();
if (exportsSeenInCell != null)
{
System.out.print(" And further exported as");
comma = false;
for(Export e : exportsSeenInCell)
{
if (comma) System.out.print(",");
comma = true;
System.out.print(" " + e.getName());
}
System.out.println();
}
}
return true;
}
}
/****************************** EXPORT CREATION ******************************/
/**
* Method to re-export all unwired/unexported ports on cell instances in the current Cell.
*/
public static void reExportAll()
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Geometric> allNodes = new ArrayList<Geometric>();
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) {
allNodes.add(it.next());
}
new ReExportNodes(cell, allNodes, false, true, false, true, User.isIncrementRightmostIndex());
}
/**
* Method to re-export everything that is selected.
* @param wiredPorts true to re-export ports that are wired.
* @param unwiredPorts true to re-export ports that are unwired.
*/
public static void reExportSelected(boolean wiredPorts, boolean unwiredPorts)
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Geometric> nodeInsts = MenuCommands.getSelectedObjects(true, false);
if (nodeInsts.size() == 0) {
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"Please select one or objects to re-export",
"Re-export failed", JOptionPane.ERROR_MESSAGE);
return;
}
new ReExportNodes(cell, nodeInsts, wiredPorts, unwiredPorts, false, true, User.isIncrementRightmostIndex());
}
/**
* Method to reexport the selected port on other nodes in the cell.
*/
public static void reExportSelectedPort()
{
// make sure there is a current cell
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
Highlighter highlighter = wnd.getHighlighter();
if (highlighter == null) return;
Highlight high = highlighter.getOneHighlight();
if (high == null || !high.isHighlightEOBJ() || !(high.getElectricObject() instanceof PortInst))
{
System.out.println("Must first select a single node and its port");
return;
}
PortInst pi = (PortInst)high.getElectricObject();
PortProto pp = pi.getPortProto();
NodeInst ni = pi.getNodeInst();
// make a list of ports to reexport
List<PortInst> queuedExports = new ArrayList<PortInst>();
Cell cell = ni.getParent();
for(Iterator<NodeInst>it = cell.getNodes(); it.hasNext(); )
{
NodeInst oNi = it.next();
if (oNi.getProto() != ni.getProto()) continue;
boolean unexported = true;
for(Iterator<Export> eIt = oNi.getExports(); eIt.hasNext(); )
{
Export e = eIt.next();
if (e.getOriginalPort().getPortProto() == pp)
{
unexported = false;
break;
}
}
if (unexported)
{
PortInst oPi = oNi.findPortInstFromProto(pp);
queuedExports.add(oPi);
}
}
// create job
new ReExportPorts(cell, queuedExports, true, false, true, false, User.isIncrementRightmostIndex(), null);
}
/**
* Method to re-export all unwired/unexported ports on cell instances in the current Cell.
* Only works for power and ground ports.
*/
public static void reExportPowerAndGround()
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Geometric> allNodes = new ArrayList<Geometric>();
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) {
allNodes.add(it.next());
}
new ReExportNodes(cell, allNodes, false, true, true, true, User.isIncrementRightmostIndex());
}
/**
* Helper class for re-exporting ports on nodes.
*/
private static class ReExportNodes extends Job
{
private Cell cell;
private List<Geometric> nodeInsts;
private boolean wiredPorts;
private boolean unwiredPorts;
private boolean onlyPowerGround;
private boolean ignorePrimitives;
private boolean fromRight;
/**
* @see ExportChanges#reExportNodes(java.util.List, boolean, boolean, boolean)
*/
public ReExportNodes(Cell cell, List<Geometric> nodeInsts, boolean wiredPorts, boolean unwiredPorts,
boolean onlyPowerGround, boolean ignorePrimitives, boolean fromRight)
{
super("Re-export nodes", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.nodeInsts = nodeInsts;
this.wiredPorts = wiredPorts;
this.unwiredPorts = unwiredPorts;
this.onlyPowerGround = onlyPowerGround;
this.ignorePrimitives = ignorePrimitives;
this.fromRight = fromRight;
startJob();
}
public boolean doIt() throws JobException
{
// disallow port action if lock is on
if (CircuitChangeJobs.cantEdit(cell, null, true, true, true) != 0) return false;
int num = reExportNodes(cell, nodeInsts, wiredPorts, unwiredPorts, onlyPowerGround, ignorePrimitives, fromRight);
System.out.println(num+" ports exported.");
return true;
}
}
/**
* Re-exports ports on each NodeInst in the list, in the order the nodeinsts appear
* in the list. Sorts the exports on each node before exporting them to make sure they
* get the correct bus indices at the next level up.
* @param cell the cell in which exporting is happening.
* @param nodeInsts a list of NodeInsts whose ports will be exported
* @param wiredPorts true to include ports that have wire connections
* @param unwiredPorts true to include ports that do not have wire connections
* @param onlyPowerGround true to only export power and ground type ports
* @param ignorePrimitives true to ignore primitive nodes
* @param fromRight true to increment the rightmost index of multidimensional arrays.
* @return the number of exports created
*/
public static int reExportNodes(Cell cell, List<Geometric> nodeInsts, boolean wiredPorts, boolean unwiredPorts,
boolean onlyPowerGround, boolean ignorePrimitives, boolean fromRight)
{
int total = 0;
for (Geometric geom : nodeInsts)
{
NodeInst ni = (NodeInst)geom;
// only look for cells, not primitives
if (ignorePrimitives)
if (!ni.isCellInstance()) continue;
// ignore recursive references (showing icon in contents)
if (ni.isIconOfParent()) continue;
List<PortInst> portInstsToExport = new ArrayList<PortInst>();
for(Iterator<PortInst> pIt = ni.getPortInsts(); pIt.hasNext(); ) {
PortInst pi = pIt.next();
// ignore if already exported
boolean found = false;
for(Iterator<Export> eIt = ni.getExports(); eIt.hasNext(); )
{
Export pp = eIt.next();
if (pp.getOriginalPort() == pi) { found = true; break; }
}
if (found) continue;
// add pi to list of ports to export
portInstsToExport.add(pi);
}
total += reExportPorts(cell, portInstsToExport, true, wiredPorts, unwiredPorts, onlyPowerGround,
fromRight, null);
}
return total;
}
/**
* Helper class for re-exporting a port on a node.
*/
public static class ReExportPorts extends Job
{
private Cell cell;
private List<PortInst> portInsts;
private boolean sort;
private boolean wiredPorts;
private boolean unwiredPorts;
private boolean onlyPowerGround;
private boolean fromRight;
private Map<PortInst,Export> originalExports;
/**
* Constructor.
*/
public ReExportPorts(Cell cell, List<PortInst> portInsts, boolean sort, boolean wiredPorts, boolean unwiredPorts,
boolean onlyPowerGround, boolean fromRight, Map<PortInst,Export> originalExports)
{
super("Re-export ports", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.portInsts = portInsts;
this.wiredPorts = wiredPorts;
this.unwiredPorts = unwiredPorts;
this.onlyPowerGround = onlyPowerGround;
this.fromRight = fromRight;
this.sort = sort;
this.originalExports = originalExports;
startJob();
}
public boolean doIt() throws JobException
{
// disallow port action if lock is on
if (CircuitChangeJobs.cantEdit(cell, null, true, true, true) != 0) return false;
int num = reExportPorts(cell, portInsts, sort, wiredPorts, unwiredPorts, onlyPowerGround, fromRight, originalExports);
System.out.println(num+" ports exported.");
return true;
}
}
/****************************** EXPORT CREATION IN A HIGHLIGHTED AREA ******************************/
/**
* Method to re-export all unwired/unexported ports on cell instances in the current Cell.
* Only works in the currently highlighted area.
* @param deep true to reexport hierarchically to the bottom.
* @param wiredPorts true to reexport ports that are wired.
* @param unwiredPorts true to reexport ports that are not wired.
*/
public static void reExportHighlighted(boolean deep, boolean wiredPorts, boolean unwiredPorts)
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
EditWindow wnd = EditWindow.getCurrent();
Rectangle2D bounds = wnd.getHighlighter().getHighlightedArea(null);
if (bounds == null)
{
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"Must select area before re-exporting the highlighted area",
"Re-export failed", JOptionPane.ERROR_MESSAGE);
return;
}
// do the job of reexporting in a boundary
ERectangle eBounds = ERectangle.fromLambda(bounds);
new ReExportHighlighted(cell, eBounds, deep, wiredPorts, unwiredPorts, User.isIncrementRightmostIndex());
}
/**
* Class to Re-export the highlighted area in a Job.
*/
private static class ReExportHighlighted extends Job
{
private Cell cell;
private ERectangle bounds;
private boolean deep;
private boolean wiredPorts;
private boolean unwiredPorts;
private boolean fromRight;
public ReExportHighlighted(Cell cell, ERectangle bounds, boolean deep, boolean wiredPorts,
boolean unwiredPorts, boolean fromRight)
{
super("Re-export highlighted", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.bounds = bounds;
this.deep = deep;
this.wiredPorts = wiredPorts;
this.unwiredPorts = unwiredPorts;
this.fromRight = fromRight;
startJob();
}
public boolean doIt() throws JobException
{
// disallow port action if lock is on
if (CircuitChangeJobs.cantEdit(cell, null, true, true, true) != 0) return false;
reExportInBounds(cell, bounds, deep, wiredPorts, unwiredPorts, true, fromRight);
return true;
}
}
/**
* Helper method to recursively re-export everything in a highlighted area.
* @param cell the Cell in which to re-export.
* @param bounds the area of the Cell to re-export.
* @param deep true to recurse down to subcells and re-export.
* @param wiredPorts true to re-export when the port is wired.
* @param unwiredPorts true to re-export when the port is not wired.
* @param topLevel true if this is the top-level call.
*/
private static void reExportInBounds(Cell cell, Rectangle2D bounds, boolean deep, boolean wiredPorts,
boolean unwiredPorts, boolean topLevel, boolean fromRight)
{
// find all ports in highlighted area
List<PortInst> queuedExports = new ArrayList<PortInst>();
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue;
// see if the cell intersects the bounds
Rectangle2D cellBounds = ni.getBounds();
if (!bounds.intersects(cellBounds)) continue;
// if doing a deep reexport, recurse into the cell
if (deep)
{
AffineTransform goIn = ni.translateIn(ni.rotateIn());
Rectangle2D boundsInside = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY(),
bounds.getWidth(), bounds.getHeight());
DBMath.transformRect(boundsInside, goIn);
reExportInBounds((Cell)ni.getProto(), boundsInside, deep, wiredPorts, unwiredPorts, false,
fromRight);
}
for (Iterator<PortInst> pIt = ni.getPortInsts(); pIt.hasNext(); )
{
PortInst pi = pIt.next();
// make sure the port is inside the selected area
Poly portPoly = pi.getPoly();
if (!bounds.contains(portPoly.getCenterX(), portPoly.getCenterY())) continue;
queuedExports.add(pi);
}
}
// remove already-exported ports
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export pp = (Export)it.next();
PortInst pi = pp.getOriginalPort();
queuedExports.remove(pi);
}
// no ports to export
if (queuedExports.size() == 0)
{
if (topLevel) System.out.println("No ports in area to export");
return;
}
// create job
int num = reExportPorts(cell, queuedExports, true, wiredPorts, unwiredPorts, false, fromRight, null);
System.out.println(num+" ports exported.");
}
/**
* Re-exports the PortInsts in the list. If sort is true, it first sorts the list by name and
* bus index. Otherwise, they are exported in the order they are found in the list.
* Note that ports are filtered first, then sorted.
* @param cell the cell in which exporting is happening.
* @param portInsts the list of PortInsts to export
* @param sort true to re-sort the portInsts list
* @param wiredPorts true to export ports that are already wired
* @param unwiredPorts true to export ports that are not already wired
* @param onlyPowerGround true to only export ports that are power and ground
* @param fromRight true to increment the rightmost index of multidimensional arrays.
* @param originalExports a map from the entries in portInsts to original Exports.
* This is used when re-exporting ports on a copy that were exported on the original.
* Ignored if null.
* @return the number of ports exported
*/
public static int reExportPorts(Cell cell, List<PortInst> portInsts, boolean sort, boolean wiredPorts,
boolean unwiredPorts, boolean onlyPowerGround, boolean fromRight,
Map<PortInst,Export> originalExports)
{
EDatabase.serverDatabase().checkChanging();
// filter the ports - remove unwanted
List<PortInst> portInstsFiltered = new ArrayList<PortInst>();
for (PortInst pi : portInsts)
{
// decide whether connections on the port should exclude its reexporting
if (pi.hasConnections())
{
if (!wiredPorts) continue;
} else
{
if (!unwiredPorts) continue;
}
if (onlyPowerGround)
{
// remove ports that are not power or ground
PortProto pp = pi.getPortProto();
if (!pp.isPower() && !pp.isGround()) continue;
}
// remove exported ports
NodeInst ni = pi.getNodeInst();
for (Iterator<Export> exit = ni.getExports(); exit.hasNext(); )
{
Export e = exit.next();
if (e.getOriginalPort() == pi) continue;
}
portInstsFiltered.add(pi);
}
// sort the accepted ports by name and bus index
if (sort)
Collections.sort(portInstsFiltered, new PortInstsSortedByBusIndex());
// remember port names already used
Set<String> already = new HashSet<String>();
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export e = (Export)it.next();
already.add(e.getNameKey().toString());
}
// export the ports
Map<String,GenMath.MutableInteger> nextPlainIndex = new HashMap<String,GenMath.MutableInteger>();
int total = 0;
for (PortInst pi : portInstsFiltered)
{
// disallow port action if lock is on
int errorCode = CircuitChangeJobs.cantEdit(cell, pi.getNodeInst(), true, true, true);
if (errorCode < 0) break;
if (errorCode > 0) continue;
// presume the name and characteristic of the new Export
Name protoName = pi.getPortProto().getNameKey();
PortCharacteristic pc = pi.getPortProto().getCharacteristic();
// or use original export name/characteristic if there is one
Export refExport = null;
if (originalExports != null)
{
refExport = originalExports.get(pi);
if (refExport != null)
{
protoName = refExport.getNameKey();
pc = refExport.getCharacteristic();
}
}
// if the node is arrayed, extend the range of the export
int busWidth = pi.getNodeInst().getNameKey().busWidth();
if (busWidth > 1)
{
// scalar export on arrayed node: make the export an array
if (NetworkTool.isBusAscending())
{
protoName = Name.findName(protoName.toString() + "[0:" + (busWidth-1) + "]");
} else
{
protoName = Name.findName(protoName.toString() + "[" + (busWidth-1) + ":0]");
}
}
// get unique name here so Export.newInstance doesn't print message
String protoNameString = protoName.toString();
protoNameString = ElectricObject.uniqueObjectName(protoNameString, cell, Export.class, already,
nextPlainIndex, false, fromRight);
// create export
Export newPp = Export.newInstance(cell, pi, protoNameString, pc);
if (newPp != null)
{
// copy text descriptor, var, and characteristic
if (pi.getPortProto() instanceof Export)
{
newPp.copyTextDescriptorFrom((Export)pi.getPortProto(), Export.EXPORT_NAME);
newPp.copyVarsFrom(((Export)pi.getPortProto()));
}
// find original export if any, and copy text descriptor, vars, and characteristic
if (refExport != null)
{
newPp.copyTextDescriptorFrom(refExport, Export.EXPORT_NAME);
newPp.copyVarsFrom(refExport);
newPp.setCharacteristic(refExport.getCharacteristic());
}
total++;
already.add(newPp.getNameKey().toString());
}
}
return total;
}
/**
* This returns the port inst on newNi that corresponds to the portinst that has been exported
* as 'referenceExport' on some other nodeinst of the same node prototype.
* This method is useful when re-exporting ports on copied nodes because
* the original port was exported.
* @param newNi the new node inst on which the port inst will be found
* @param referenceExport the export on the old node inst
* @return the port inst on newNi which corresponds to the exported portinst on the oldNi
* referred to through 'referenceExport'.
*/
public static PortInst getNewPortFromReferenceExport(NodeInst newNi, Export referenceExport)
{
PortInst origPi = referenceExport.getOriginalPort();
PortInst newPi = newNi.findPortInstFromProto(origPi.getPortProto());
return newPi;
}
/****************************** EXPORT DELETION ******************************/
/**
* Method to return a list of selected exports.
* If none are selected, the list is empty.
*/
private static List<Export> getSelectedExports()
{
List<Export> selectedExports = new ArrayList<Export>();
EditWindow wnd = EditWindow.getCurrent();
List<DisplayedText> dts = wnd.getHighlighter().getHighlightedText(true);
for(DisplayedText dt : dts)
{
if (dt.getElectricObject() instanceof Export)
{
Export pp = (Export)dt.getElectricObject();
selectedExports.add(pp);
}
}
return selectedExports;
}
/**
* Method to delete the currently selected exports.
*/
public static void deleteExport()
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Export> exportsToDelete = getSelectedExports();
if (exportsToDelete.size() == 0)
{
System.out.println("There are no selected exports to delete");
return;
}
deleteExports(cell, exportsToDelete);
}
/**
* Method to delete all exports on the highlighted objects.
*/
public static void deleteExportsOnSelected()
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Export> exportsToDelete = new ArrayList<Export>();
EditWindow wnd = EditWindow.getCurrent();
List<Geometric> highs = wnd.getHighlighter().getHighlightedEObjs(true, false);
for(Geometric geom : highs)
{
NodeInst ni = (NodeInst)geom;
for(Iterator<Export> eIt = ni.getExports(); eIt.hasNext(); )
{
exportsToDelete.add(eIt.next());
}
}
if (exportsToDelete.size() == 0)
{
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"There are no exports on the highlighted objects",
"Re-export failed", JOptionPane.ERROR_MESSAGE);
return;
}
deleteExports(cell, exportsToDelete);
}
/**
* Method to delete all exports in the highlighted area.
*/
public static void deleteExportsInArea()
{
// make sure there is a current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<Export> exportsToDelete = new ArrayList<Export>();
EditWindow wnd = EditWindow.getCurrent();
Rectangle2D bounds = wnd.getHighlighter().getHighlightedArea(null);
if (bounds == null)
{
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"Must select something before deleting the highlighted exports",
"Export delete failed", JOptionPane.ERROR_MESSAGE);
return;
}
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
for(Iterator<Export> eIt = ni.getExports(); eIt.hasNext(); )
{
Export e = eIt.next();
PortInst pi = e.getOriginalPort();
Poly poly = pi.getPoly();
if (bounds.contains(poly.getCenterX(), poly.getCenterY()))
exportsToDelete.add(e);
}
}
if (exportsToDelete.size() == 0)
{
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"There are no exports in the highlighted area",
"Re-export failed", JOptionPane.ERROR_MESSAGE);
return;
}
deleteExports(cell, exportsToDelete);
}
public static void deleteExports(Cell cell, List<Export> exportsToDelete)
{
// disallow port action if lock is on
if (CircuitChangeJobs.cantEdit(cell, null, true, false, false) != 0) return;
Set<Export> exportsConfirmed = new HashSet<Export>();
for(Export e : exportsToDelete)
{
int errorCode = CircuitChangeJobs.cantEdit(cell, e.getOriginalPort().getNodeInst(), true, true, false);
if (errorCode < 0) break;
if (errorCode > 0) continue;
exportsConfirmed.add(e);
}
if (exportsConfirmed.isEmpty())
{
System.out.println("No exports deleted");
return;
}
new DeleteExports(cell, exportsConfirmed);
}
private static class DeleteExports extends Job
{
private Cell cell;
private Set<Export> exportsToDelete;
public DeleteExports(Cell cell, Set<Export> exportsToDelete)
{
super("Delete exports", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.exportsToDelete = exportsToDelete;
startJob();
}
public boolean doIt() throws JobException
{
cell.killExports(exportsToDelete);
System.out.println(exportsToDelete.size() + " exports deleted");
return true;
}
}
/****************************** EXPORT MOVING ******************************/
/**
* Method to move the currently selected export from one node to another.
*/
public static void moveExport()
{
Export source = null;
PortInst dest = null;
EditWindow wnd = EditWindow.getCurrent();
for(Highlight h : wnd.getHighlighter().getHighlights())
{
boolean used = false;
if (h.isHighlightEOBJ())
{
if (h.getElectricObject() instanceof PortInst)
{
if (dest != null)
{
System.out.println("Must select only one node-port as a destination of the move");
return;
}
dest = (PortInst)h.getElectricObject();
used = true;
}
} else if (h.isHighlightText())
{
if (h.getVarKey() == Export.EXPORT_NAME && h.getElectricObject() instanceof Export)
{
source = (Export)h.getElectricObject();
used = true;
}
}
if (!used)
{
System.out.println("Moving exports: select one export to move, and one node-port as its destination");
return;
}
}
if (source == null || dest == null)
{
System.out.println("First select one export to move, and one node-port as its destination");
return;
}
new MoveExport(source, dest);
}
private static class MoveExport extends Job
{
private Export source;
private PortInst dest;
protected MoveExport(Export source, PortInst dest)
{
super("Move export", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.source = source;
this.dest = dest;
startJob();
}
public boolean doIt() throws JobException
{
source.move(dest);
return true;
}
}
/****************************** EXPORT RENAMING ******************************/
/**
* Method to rename the currently selected export.
*/
public static void renameExport()
{
EditWindow wnd = EditWindow.getCurrent();
Highlight h = wnd.getHighlighter().getOneHighlight();
if (h == null || h.getVarKey() != Export.EXPORT_NAME || !(h.getElectricObject() instanceof Export))
{
System.out.println("Must select an export name before renaming it");
return;
}
Export pp = (Export)h.getElectricObject();
String response = JOptionPane.showInputDialog(Main.getCurrentJFrame(), "Rename export", pp.getName());
if (response == null) return;
new RenameExport(pp, response);
}
/**
* Class to rename an export in a new thread.
*/
public static class RenameExport extends Job
{
private Export pp;
private String newName;
public RenameExport(Export pp, String newName)
{
super("Rename Export" + pp.getName(), User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.pp = pp;
this.newName = newName;
startJob();
}
public boolean doIt() throws JobException
{
pp.rename(newName);
return true;
}
}
/**
* Class to change the characteristic of an export in a new thread.
*/
public static class ChangeExportCharacteristic extends Job
{
private Export pp;
private PortCharacteristic newCh;
public ChangeExportCharacteristic(Export pp, PortCharacteristic newCh)
{
super("Change Export Characteristics " + pp.getName(), User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.pp = pp;
this.newCh = newCh;
startJob();
}
public boolean doIt() throws JobException
{
pp.setCharacteristic(newCh);
return true;
}
}
/**
* Class to change the body-only flag of an export in a new thread.
*/
public static class ChangeExportBodyOnly extends Job
{
private Export pp;
private boolean bo;
public ChangeExportBodyOnly(Export pp, boolean bo)
{
super("Change Export Body-Only " + pp.getName(), User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.pp = pp;
this.bo = bo;
startJob();
}
public boolean doIt() throws JobException
{
pp.setBodyOnly(bo);
return true;
}
}
/**
* Class to rename a list of Exports with numeric suffixes in a new thread.
*/
public static class RenumberNumericExports extends Job
{
private List<Export> exports;
public RenumberNumericExports(List<Export> exports)
{
super("Rename Numeric Exports", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.exports = exports;
startJob();
}
public boolean doIt() throws JobException
{
Collections.sort(exports, new ExportsByNumber());
String lastPureName = "";
int lastIndex = 0;
for(Export e : exports)
{
String name = e.getName();
int numberPos = name.length();
while (numberPos > 0 && Character.isDigit(name.charAt(numberPos-1))) numberPos--;
int nameEnd = numberPos;
if (nameEnd > 0 && name.charAt(nameEnd-1) == '_') nameEnd--;
String pureName = name.substring(0, nameEnd);
if (!pureName.equals(lastPureName))
lastIndex = 0;
lastPureName = pureName;
String newName = pureName;
if (lastIndex > 0) newName += "_" + lastIndex;
lastIndex++;
if (!newName.equals(name))
e.rename(newName);
}
return true;
}
}
/**
* Comparator class for sorting Export by their name with number considered.
*/
public static class ExportsByNumber implements Comparator<Export>
{
/**
* Method to sort Exports by their name.
*/
public int compare(Export e1, Export e2)
{
String s1 = e1.getName();
String s2 = e2.getName();
return TextUtils.STRING_NUMBER_ORDER.compare(s1, s2);
}
}
/****************************** EXPORT HIGHLIGHTING ******************************/
private static class ShownPorts
{
Point2D loc;
PortProto pp;
int angle;
}
/**
* Method to show all exports in the current cell.
*/
public static void showExports()
{
showPortsAndExports(null);
}
/**
* Method to show all ports on the selected nodes in the current cell.
*/
public static void showPorts()
{
EditWindow wnd = EditWindow.getCurrent();
List<Geometric> nodes = wnd.getHighlighter().getHighlightedEObjs(true, false);
if (nodes == null || nodes.size() == 0)
{
System.out.println("No nodes are highlighted");
return;
}
showPortsAndExports(nodes);
}
private static void showPortsAndExports(List<Geometric> nodes)
{
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
Cell cell = wnd.getCell();
if (cell == null)
{
System.out.println("No cell in this window");
return;
}
// determine the maximum number of ports to show
int total = cell.getNumPorts();
if (nodes != null)
{
total = 0;
for(Geometric geom : nodes)
{
NodeInst ni = (NodeInst)geom;
total += ni.getNumPortInsts();
}
}
// associate ports with display locations (and compute the true number of ports to show)
Rectangle2D displayable = wnd.displayableBounds();
ShownPorts [] portList = new ShownPorts[total];
total = 0;
int ignored = 0;
if (nodes == null)
{
// handle exports on the cell
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export pp = (Export)it.next();
Poly poly = pp.getPoly();
Point2D ptOut = new Point2D.Double(poly.getCenterX(), poly.getCenterY());
if (ptOut.getX() < displayable.getMinX() || ptOut.getX() > displayable.getMaxX() ||
ptOut.getY() < displayable.getMinY() || ptOut.getY() > displayable.getMaxY())
{
ignored++;
continue;
}
portList[total] = new ShownPorts();
portList[total].loc = new Point2D.Double(poly.getCenterX(), poly.getCenterY());
portList[total].pp = pp;
total++;
}
} else
{
// handle ports on the selected nodes
for(Geometric geom : nodes)
{
NodeInst ni = (NodeInst)geom;
for(Iterator<PortInst> pIt = ni.getPortInsts(); pIt.hasNext(); )
{
PortInst pi = pIt.next();
Poly poly = pi.getPoly();
Point2D ptOut = new Point2D.Double(poly.getCenterX(), poly.getCenterY());
if (ptOut.getX() < displayable.getMinX() || ptOut.getX() > displayable.getMaxX() ||
ptOut.getY() < displayable.getMinY() || ptOut.getY() > displayable.getMaxY())
{
ignored++;
continue;
}
portList[total] = new ShownPorts();
portList[total].loc = new Point2D.Double(poly.getCenterX(), poly.getCenterY());
portList[total].pp = pi.getPortProto();
total++;
}
}
}
// determine the height of text in screen space
int fontSize = EditWindow.getDefaultFontSize();
Point screenOrigin = wnd.databaseToScreen(0, 0);
Point2D thPoint = wnd.screenToDatabase(screenOrigin.x, screenOrigin.y + fontSize);
double textHeight = Math.abs(thPoint.getY());
// determine the location of the port labels
Point2D [] labelLocs = new Point2D.Double[total];
double digitIndentX = displayable.getWidth() / 20;
double digitIndentY = displayable.getHeight() / 20;
int numPerSide = (total + 3) / 4;
int leftSideCount, topSideCount, rightSideCount, botSideCount;
leftSideCount = topSideCount = rightSideCount = botSideCount = numPerSide;
if (leftSideCount + topSideCount + rightSideCount + botSideCount > total)
botSideCount--;
if (leftSideCount + topSideCount + rightSideCount + botSideCount > total)
topSideCount--;
if (leftSideCount + topSideCount + rightSideCount + botSideCount > total)
rightSideCount--;
int fill = 0;
for(int i=0; i<leftSideCount; i++)
{
labelLocs[fill++] = new Point2D.Double(displayable.getMinX() + digitIndentX,
displayable.getHeight() / (leftSideCount+1) * (i+1) + displayable.getMinY());
}
for(int i=0; i<topSideCount; i++)
{
double shift = (i % 3) * textHeight - textHeight;
labelLocs[fill++] = new Point2D.Double(displayable.getWidth() / (topSideCount+1) * (i+1) + displayable.getMinX(),
displayable.getMaxY() - digitIndentY - shift);
}
for(int i=0; i<rightSideCount; i++)
{
labelLocs[fill++] = new Point2D.Double(displayable.getMaxX() - digitIndentX,
displayable.getMaxY() - displayable.getHeight() / (rightSideCount+1) * (i+1));
}
for(int i=0; i<botSideCount; i++)
{
double shift = (i % 3) * textHeight - textHeight;
labelLocs[fill++] = new Point2D.Double(displayable.getMaxX() - displayable.getWidth() / (botSideCount+1) * (i+1),
displayable.getMinY() + digitIndentY - shift);
}
// build a sorted list of ports around the center
double x = 0, y = 0;
for(int i=0; i<total; i++)
{
x += portList[i].loc.getX();
y += portList[i].loc.getY();
}
Point2D center = new Point2D.Double(x / total, y / total);
for(int i=0; i<total; i++)
{
if (center.getX() == portList[i].loc.getX() && center.getY() == portList[i].loc.getY())
portList[i].angle = 0; else
portList[i].angle = -DBMath.figureAngle(center, portList[i].loc);
}
List<ShownPorts> portLabels = new ArrayList<ShownPorts>();
for(int i=0; i<total; i++)
portLabels.add(portList[i]);
Collections.sort(portLabels, new SortPortAngle());
total = 0;
for(ShownPorts sp : portLabels)
portList[total++] = sp;
// figure out the best rotation offset
double bestDist = 0;
int bestOff = 0;
for(int i=0; i<total; i++)
{
double dist = 0;
for(int j=0; j<total; j++)
dist += labelLocs[j].distance(portList[(j+i)%total].loc);
if (dist < bestDist || i == 0)
{
bestOff = i;
bestDist = dist;
}
}
// show the ports
Highlighter highlighter = wnd.getHighlighter();
Font font = wnd.getFont(null);
FontRenderContext frc = new FontRenderContext(null, true, true);
LineMetrics lm = font.getLineMetrics("hy", frc);
double baselineVer = wnd.getTextUnitSize(lm.getDescent());
double baselineHor = wnd.getTextUnitSize(2);
for(int i=0; i<total; i++)
{
int index = (bestOff + i) % total;
Point2D loc = labelLocs[i];
String msg = portList[index].pp.getName();
// get the connecting-line coordinates in screen space
Point locationLabel = wnd.databaseToScreen(loc.getX(), loc.getY());
Point locationPort = wnd.databaseToScreen(portList[index].loc.getX(), portList[index].loc.getY());
// determine the opposite corner of the text
GlyphVector v = font.createGlyphVector(frc, msg);
Rectangle2D glyphBounds = v.getLogicalBounds();
int otherX = locationLabel.x + (int)glyphBounds.getWidth();
int otherY = locationLabel.y - (int)glyphBounds.getHeight();
Point2D locOther = wnd.screenToDatabase(otherX, otherY);
// if the text is off-screen, adjust it
if (otherX > wnd.getSize().width)
{
int offDist = otherX - wnd.getSize().width;
locationLabel.x -= offDist;
otherX -= offDist;
loc = wnd.screenToDatabase(locationLabel.x, locationLabel.y);
}
// change the attachment point on the label to be closest to the port
if (Math.abs(locationPort.x-locationLabel.x) > Math.abs(locationPort.x-otherX))
locationLabel.x = otherX;
if (Math.abs(locationPort.y-locationLabel.y) > Math.abs(locationPort.y-otherY))
locationLabel.y = otherY;
// convert this shift back to database units for the highlight
Point2D locLineEnd = wnd.screenToDatabase(locationLabel.x, locationLabel.y);
// draw the port name
highlighter.addMessage(cell, msg, new Point2D.Double(loc.getX()+baselineHor, loc.getY()+baselineVer));
// draw a box around the text
Point2D odd1 = new Point2D.Double(loc.getX(), locOther.getY());
Point2D odd2 = new Point2D.Double(locOther.getX(), loc.getY());
highlighter.addLine(loc, odd1, cell);
highlighter.addLine(odd1, locOther, cell);
highlighter.addLine(locOther, odd2, cell);
highlighter.addLine(odd2, loc, cell);
// draw a line from the text to the port
highlighter.addLine(locLineEnd, portList[index].loc, cell);
}
highlighter.finished();
System.out.println(total + " exported ports to show");
if (ignored > 0)
System.out.println("Could not display " + ignored + " ports (outside of the window)");
}
private static class SortPortAngle implements Comparator<ShownPorts>
{
public int compare(ShownPorts s1, ShownPorts s2)
{
return s1.angle - s2.angle;
}
}
/****************************** EXPORT MATCHING BETWEEN LIBRARIES ******************************/
/**
* Method to synchronize the exports in two libraries.
* The user is prompted for another library (other than the current one)
* and all exports in that library are copied to the current one.
*/
public static void synchronizeLibrary()
{
List<Library> libs = Library.getVisibleLibraries();
Library curLib = Library.getCurrent();
int otherLibraries = libs.size() - 1;
if (otherLibraries < 1)
{
System.out.println("There must be an other library (not the current one) from which to copy exports.");
return;
}
String [] libNames = new String[otherLibraries];
int i=0;
for (Library oLib : libs)
{
if (oLib == curLib) continue;
libNames[i++] = oLib.getName();
}
String chosen = (String)JOptionPane.showInputDialog(Main.getCurrentJFrame(),
"Choose another library from which to copy exports", "Choose a Library",
JOptionPane.QUESTION_MESSAGE, null, libNames, libNames[0]);
if (chosen == null) return;
Library oLib = Library.findLibrary(chosen);
if (oLib == null) return;
// now run the synchronization
new SynchronizeExports(oLib);
}
/**
* Class to synchronize exports in a separate Job.
*/
private static class SynchronizeExports extends Job
{
private Library oLib;
private SynchronizeExports(Library oLib)
{
super("Synchronize exports", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.oLib = oLib;
startJob();
}
public boolean doIt() throws JobException
{
// merge the two libraries
int newPorts = 0;
boolean noCells = false;
Library curLib = Library.getCurrent();
for(Iterator<Cell> cIt = curLib.getCells(); cIt.hasNext(); )
{
Cell np = cIt.next();
// find this cell in the other library
for(Iterator<Cell> oCIt = oLib.getCells(); oCIt.hasNext(); )
{
Cell oNp = oCIt.next();
if (!np.getName().equals(oNp.getName())) continue;
// synchronize the ports
for(Iterator<PortProto> pIt = oNp.getPorts(); pIt.hasNext(); )
{
Export oPp = (Export)pIt.next();
// see if that other cell's port is in this one
Export pp = (Export)np.findPortProto(oPp.getName());
if (pp != null) continue;
// must add port "oPp" to cell "np"
NodeInst oNi = oPp.getOriginalPort().getNodeInst();
if (oNi.isCellInstance())
{
if (!noCells)
System.out.println("Cannot yet make exports that come from other cell instances (i.e. export " +
oPp.getName() + " in " + oNp + ")");
noCells = true;
continue;
}
// presume that the cells have the same coordinate system
NodeInst ni = NodeInst.makeInstance(oNi.getProto(), oNi.getAnchorCenter(), oNi.getXSize(), oNi.getYSize(),
np, oNi.getOrient(), null, oNi.getTechSpecific());
if (ni == null) continue;
PortInst pi = ni.findPortInstFromProto(oPp.getOriginalPort().getPortProto());
pp = Export.newInstance(np, pi, oPp.getName(), oPp.getCharacteristic());
if (pp == null) continue;
pp.copyTextDescriptorFrom(oPp, Export.EXPORT_NAME);
pp.copyVarsFrom(oPp);
newPorts++;
}
}
}
System.out.println("Created " + newPorts + " new exports in current " + curLib);
return true;
}
}
/****************************** REPLACING CELL INSTANCES FROM ANOTHER LIBRARY ******************************/
/**
* Method to replace all cell instances in the current cell with like-named
* ones from another library.
*/
public static void replaceFromOtherLibrary()
{
Cell curCell = WindowFrame.needCurCell();
if (curCell == null) return;
List<Library> libs = Library.getVisibleLibraries();
Library curLib = Library.getCurrent();
int otherLibraries = libs.size() - 1;
if (otherLibraries < 1)
{
System.out.println("There must be an other library (not the current one) from which to replace cells.");
return;
}
String [] libNames = new String[otherLibraries];
int i=0;
for (Library oLib : libs)
{
if (oLib == curLib) continue;
libNames[i++] = oLib.getName();
}
String chosen = (String)JOptionPane.showInputDialog(Main.getCurrentJFrame(),
"Choose another library from which to replace cell instances", "Choose a Library",
JOptionPane.QUESTION_MESSAGE, null, libNames, libNames[0]);
if (chosen == null) return;
Library oLib = Library.findLibrary(chosen);
if (oLib == null) return;
// now run the replacement
new ReplaceFromOtherLibrary(curCell, oLib);
}
/**
* Class to replace all cell instances in the current cell with like-named
* ones from another library.
*/
private static class ReplaceFromOtherLibrary extends Job
{
private Cell cell;
private Library oLib;
private ReplaceFromOtherLibrary(Cell cell, Library oLib)
{
super("Replace Cell Instances From Another Library", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.oLib = oLib;
startJob();
}
public boolean doIt() throws JobException
{
Map<NodeInst,Cell> cellsToReplace = new HashMap<NodeInst,Cell>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue;
if (ni.getXSize() != 0 || ni.getYSize() != 0) continue;
Cell oldType = (Cell)ni.getProto();
if (oldType.getLibrary() == oLib) continue;
String nameToFind = oldType.getName();
if (oldType.getView() != View.UNKNOWN) nameToFind += oldType.getView().getAbbreviationExtension();
Cell newType = oLib.findNodeProto(nameToFind);
if (newType != null)
{
cellsToReplace.put(ni, newType);
}
}
System.out.println("Changing " + cellsToReplace.size() + " cell instances...");
int replacements = 0;
for(Iterator <NodeInst> it = cellsToReplace.keySet().iterator(); it.hasNext(); )
{
NodeInst ni = it.next();
Cell newType = cellsToReplace.get(ni);
ni.replace(newType, true, true);
replacements++;
}
System.out.println("Changed " + replacements + " cell instances");
return true;
}
}
}