/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CircuitChanges.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.ImmutableArcInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.geometry.Dimension2D;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.text.Pref;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
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.topology.RTNode;
import com.sun.electric.database.variable.DisplayedText;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.lib.LibFile;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.io.input.LibraryFiles;
import com.sun.electric.tool.project.Project;
import com.sun.electric.tool.user.menus.MenuCommands;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.LayerVisibility;
import com.sun.electric.tool.user.ui.MessagesWindow;
import com.sun.electric.tool.user.ui.OutlineListener;
import com.sun.electric.tool.user.ui.ToolBar;
import com.sun.electric.tool.user.ui.WindowContent;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.tool.user.waveform.WaveformWindow;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.GenMath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
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;
/**
* Class for user-level changes to the circuit.
*/
public class CircuitChanges
{
// constructor, never used
private CircuitChanges() {}
/****************************** NODE TRANSFORMATION ******************************/
private static double lastRotationAmount = 90;
/**
* Method to handle the command to rotate the selected objects by an amount.
* @param amount the amount to rotate. If the amount is zero, prompt for an amount.
*/
public static void rotateObjects(int amount)
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
// if zero rotation, prompt for amount
if (amount == 0)
{
String val = JOptionPane.showInputDialog("Amount to rotate", new Double(lastRotationAmount));
if (val == null) return;
double fAmount = TextUtils.atof(val);
if (fAmount == 0)
{
System.out.println("Null rotation amount");
return;
}
lastRotationAmount = fAmount;
amount = (int)(fAmount * 10);
}
List<Geometric> highs = MenuCommands.getSelectedObjects(true, true);
if (highs.size() == 0)
{
System.out.println("Cannot rotate: nothing is selected");
return;
}
new CircuitChangeJobs.RotateSelected(cell, highs, amount, false, false);
}
/**
* Method to handle the command to mirror the selected objects.
* @param horizontally true to mirror horizontally (about the horizontal, flipping the Y value).
* False to mirror vertically (about the vertical, flipping the X value).
*/
public static void mirrorObjects(boolean horizontally)
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Cell cell = wf.getContent().getCell();
if (cell == null) return;
new CircuitChangeJobs.RotateSelected(cell, MenuCommands.getSelectedObjects(true, true), 0, true, horizontally);
}
/****************************** NODE ALIGNMENT ******************************/
/**
* Method to align the selected objects to the grid.
*/
public static void alignToGrid()
{
// get a list of all selected nodes and arcs
List<Geometric> selected = MenuCommands.getSelectedObjects(true, true);
// make a set of selected nodes
Set<NodeInst> selectedNodes = new HashSet<NodeInst>();
for(Geometric geom : selected)
{
if (geom instanceof NodeInst) selectedNodes.add((NodeInst)geom);
}
// make a list of nodes at the ends of arcs that should be added to the list
List<NodeInst> addedNodes = new ArrayList<NodeInst>();
for(Geometric geom : selected)
{
if (!(geom instanceof ArcInst)) continue;
ArcInst ai = (ArcInst)geom;
NodeInst head = ai.getHead().getPortInst().getNodeInst();
if (!selectedNodes.contains(head))
{
addedNodes.add(head);
selectedNodes.add(head);
}
NodeInst tail = ai.getTail().getPortInst().getNodeInst();
if (!selectedNodes.contains(tail))
{
addedNodes.add(tail);
selectedNodes.add(tail);
}
}
for(NodeInst ni : addedNodes)
selected.add(ni);
if (selected.size() == 0)
{
System.out.println("Must select something before aligning it to the grid");
return;
}
Dimension2D alignment = User.getAlignmentToGrid();
if (alignment.getWidth() <= 0 || alignment.getHeight() <= 0)
{
System.out.println("No alignment given: set Alignment Options first");
return;
}
// now align them
new CircuitChangeJobs.AlignObjects(selected, alignment);
}
/**
* Method to align the selected nodes.
* @param horizontal true to align them horizontally; false for vertically.
* @param direction if horizontal is true, meaning is 0 for left, 1 for right, 2 for center.
* If horizontal is false, meaning is 0 for top, 1 for bottom, 2 for center.
*/
public static void alignNodes(boolean horizontal, int direction)
{
// make sure there is a current cell
Cell np = WindowFrame.needCurCell();
if (np == null) return;
// get the objects to be moved (mark nodes with nonzero "temp1")
List<Geometric> list = MenuCommands.getSelectedObjects(true, true);
if (list.size() == 0)
{
System.out.println("First select objects to move");
return;
}
// make sure they are all in the same cell
for(Geometric geom : list)
{
if (geom.getParent() != np)
{
System.out.println("All moved objects must be in the same cell");
return;
}
}
// count the number of nodes
List<NodeInst> nodes = new ArrayList<NodeInst>();
for(Geometric geom : list)
{
if (geom instanceof NodeInst) nodes.add((NodeInst)geom);
}
int total = nodes.size();
if (total == 0) return;
NodeInst [] nis = new NodeInst[total];
double [] dCX = new double[total];
double [] dCY = new double[total];
for(int i=0; i<total; i++)
{
nis[i] = nodes.get(i);
}
// get bounds
double lX = 0, hX = 0, lY = 0, hY = 0;
for(int i=0; i<total; i++)
{
NodeInst ni = nis[i];
Rectangle2D bounds = ni.getBounds();
if (i == 0)
{
lX = bounds.getMinX();
hX = bounds.getMaxX();
lY = bounds.getMinY();
hY = bounds.getMaxY();
} else
{
if (bounds.getMinX() < lX) lX = bounds.getMinX();
if (bounds.getMaxX() > hX) hX = bounds.getMaxX();
if (bounds.getMinY() < lY) lY = bounds.getMinY();
if (bounds.getMaxY() > hY) hY = bounds.getMaxY();
}
}
// determine motion
for(int i=0; i<total; i++)
{
NodeInst ni = nis[i];
Rectangle2D bounds = ni.getBounds();
dCX[i] = dCY[i] = 0;
if (horizontal)
{
// horizontal alignment
switch (direction)
{
case 0: // align to left
dCX[i] = lX - bounds.getMinX();
break;
case 1: // align to right
dCX[i] = hX - bounds.getMaxX();
break;
case 2: // align to center
dCX[i] = (lX + hX) / 2 - bounds.getCenterX();
break;
}
} else
{
// vertical alignment
switch (direction)
{
case 0: // align to top
dCY[i] = hY - bounds.getMaxY();
break;
case 1: // align to bottom
dCY[i] = lY - bounds.getMinY();
break;
case 2: // align to center
dCY[i] = (lY + hY) / 2 - bounds.getCenterY();
break;
}
}
}
new CircuitChangeJobs.AlignNodes(nis, dCX, dCY);
}
/****************************** ARC MODIFICATION ******************************/
/**
* This method sets the highlighted arcs to Rigid
*/
public static void arcRigidCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.RIGID, getHighlighted());
}
/**
* This method sets the highlighted arcs to Non-Rigid
*/
public static void arcNotRigidCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.NONRIGID, getHighlighted());
}
/**
* This method sets the highlighted arcs to Fixed-Angle
*/
public static void arcFixedAngleCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.FIXEDANGLE, getHighlighted());
}
/**
* This method sets the highlighted arcs to Not-Fixed-Angle
*/
public static void arcNotFixedAngleCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.NONFIXEDANGLE, getHighlighted());
}
/**
* This method toggles the directionality of highlighted arcs.
*/
public static void arcDirectionalCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.DIRECTIONAL, getHighlighted());
}
/**
* This method sets the highlighted arcs to have their head end extended.
*/
public static void arcHeadExtendCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.HEADEXTEND, getHighlighted());
}
/**
* This method sets the highlighted arcs to have their tail end extended.
*/
public static void arcTailExtendCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ChangeArcProperties(cell, CircuitChangeJobs.ChangeArcEnum.TAILEXTEND, getHighlighted());
}
/**
* This method sets the highlighted ports to be negated.
*/
public static void toggleNegatedCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ToggleNegationJob(cell, getHighlighted());
}
/**
* Get list of Highlights in current highlighter
* @return list of Highlights
*/
public static List<Highlight> getHighlighted()
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return new ArrayList<Highlight>();
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return new ArrayList<Highlight>();
return highlighter.getHighlights();
}
/**
* Method to rip the currently selected bus arc out into individual wires.
*/
public static void ripBus()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
List<ArcInst> list = MenuCommands.getSelectedArcs();
if (list.size() == 0)
{
System.out.println("Must select bus arcs to rip into individual signals");
return;
}
new CircuitChangeJobs.RipTheBus(cell, list);
}
/****************************** DELETE SELECTED OBJECTS ******************************/
/**
* Method to delete all selected objects.
*/
public static void deleteSelected()
{
// is this the messages window?
MessagesWindow mw = MessagesWindow.getFocusOwner();
if (mw != null) {
mw.clear(false);
return;
}
// see what type of window is selected
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
// for waveform windows, delete selected signals
if (wf.getContent() instanceof WaveformWindow)
{
WaveformWindow ww = (WaveformWindow)wf.getContent();
ww.deleteSelectedSignals();
return;
}
// for edit windows doing outline editing, delete the selected point
if (WindowFrame.getListener() == OutlineListener.theOne)
{
OutlineListener.theOne.deletePoint();
return;
}
// see if a highlighted area is given
boolean highlightedArea = ToolBar.getSelectMode() == ToolBar.SelectMode.AREA;
List<Highlight> highlights = highlighter.getHighlights();
if (highlights.size() == 1)
{
Highlight high = highlights.get(0);
if (high instanceof HighlightArea) highlightedArea = true;
}
if (highlightedArea)
{
EditWindow wnd = EditWindow.getCurrent();
Rectangle2D bounds = highlighter.getHighlightedArea(wnd);
if (bounds == null)
{
System.out.println("Nothing is selected");
return;
}
new CircuitChangeJobs.DeleteSelectedGeometry(cell, bounds);
} else
{
// disable the "node moves with text" because it causes nodes to be deleted with text, too
boolean formerMoveWithText = User.isMoveNodeWithExport();
Pref.delayPrefFlushing();
User.setMoveNodeWithExport(false);
// get what is highlighted
List<DisplayedText> highlightedText = highlighter.getHighlightedText(true);
List<Geometric> highlighted = highlighter.getHighlightedEObjs(true, true);
// restore "node moves with text"
User.setMoveNodeWithExport(formerMoveWithText);
Pref.resumePrefFlushing();
// delete if anything was selected
if (highlightedText.size() == 0 && highlighted.size() == 0) return;
new CircuitChangeJobs.DeleteSelected(cell, highlightedText, highlighted, User.isReconstructArcsAndExportsToDeletedCells());
}
}
public static void cellCenterToCenterOfSelection() {
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
if (CircuitChangeJobs.cantEdit(cell, null, true, false, false) != 0) return;
Highlighter highlighter = wnd.getHighlighter();
if (highlighter == null) return;
Rectangle2D bounds = highlighter.getHighlightedArea(wnd);
if (bounds == null) return;
new CircuitChangeJobs.CellCenterToCenterOfSelection(cell, new EPoint(bounds.getCenterX(), bounds.getCenterY()));
}
/**
* Method to delete arcs connected to selected nodes.
* @param both true if both ends of the arc must be selected.
*/
public static void deleteArcsOnSelected(boolean both)
{
// get the selection
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
// make sure the cell is editable
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
if (CircuitChangeJobs.cantEdit(cell, null, true, false, false) != 0) return;
// make a set of selected nodes
Highlighter highlighter = wnd.getHighlighter();
if (highlighter == null) return;
Set<NodeInst> selectedNodes = new HashSet<NodeInst>();
for(Geometric g : highlighter.getHighlightedEObjs(true, false))
selectedNodes.add((NodeInst)g);
// make a set of arcs to delete
Set<ArcInst> arcsToDelete = new HashSet<ArcInst>();
for(NodeInst ni : selectedNodes)
{
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
ArcInst ai = con.getArc();
if (both)
{
if (!selectedNodes.contains(ai.getHeadPortInst().getNodeInst())) continue;
if (!selectedNodes.contains(ai.getTailPortInst().getNodeInst())) continue;
}
arcsToDelete.add(ai);
}
}
if (arcsToDelete.size() == 0)
{
System.out.println("There are no arcs on the selected nodes that can be deleted");
return;
}
new CircuitChangeJobs.DeleteArcs(arcsToDelete);
}
/****************************** DELETE A CELL ******************************/
/**
* Method to delete a cell.
* @param cell the cell to delete.
* @param confirm true to prompt the user to confirm the deletion.
* @param quiet true not to warn the user of the cell being used.
* @return true if the cell will be deleted (in a separate Job).
*/
public static boolean deleteCell(Cell cell, boolean confirm, boolean quiet)
{
// see if this cell is in use anywhere
if (cell.isInUse("delete", quiet, true)) return false;
// make sure the user really wants to delete the cell
if (confirm)
{
int response = JOptionPane.showConfirmDialog(Main.getCurrentJFrame(),
"Are you sure you want to delete " + cell + "?", "Delete Cell Dialog", JOptionPane.YES_NO_OPTION);
if (response != JOptionPane.YES_OPTION) return false;
}
// delete references to cell
cleanCellRef(cell);
// delete the cell
new CellChangeJobs.DeleteCell(cell);
return true;
}
/**
* Method to delete cell "cell". Validity checks are assumed to be made (i.e. the
* cell is not used and is not locked).
*/
public static void cleanCellRef(Cell cell)
{
// delete random references to this cell
Library lib = cell.getLibrary();
if (cell == lib.getCurCell())
lib.setCurCell(null);
// close windows that reference this cell
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
WindowContent content = wf.getContent();
if (content == null) continue;
if (content.getCell() == cell)
{
if (!(content instanceof EditWindow))
{
wf.setCellWindow(null, null);
} else
{
content.setCell(null, null, null);
content.fullRepaint();
}
}
}
}
/****************************** RENAME CELLS ******************************/
public static void renameCellInJob(Cell cell, String newName)
{
// see if the rename should also regroup
String newGroupCell = null;
Set<Cell> set = new HashSet<Cell>();
for(Iterator<Cell> it = cell.getLibrary().getCells(); it.hasNext(); )
{
Cell oCell = it.next();
// Case when the new cell name defines a group already
if (oCell.getName().equalsIgnoreCase(newName) && oCell.getCellGroup() != cell.getCellGroup())
{
int response = JOptionPane.showConfirmDialog(Main.getCurrentJFrame(),
"Also place the cell into the \"" + oCell.getCellGroup().getName() + "\" group?");
if (response == JOptionPane.YES_OPTION) newGroupCell = oCell.getName();
break;
}
}
set.add(cell);
if (cell.getNumVersions() > 1) // more than 1 version
{
int response = JOptionPane.showConfirmDialog(Main.getCurrentJFrame(),
"Also rename previous versions of the cell \"" + cell.getName() + "\" ?");
if (response == JOptionPane.YES_OPTION)
{
for(Iterator<Cell> it = cell.getVersions(); it.hasNext(); )
{
set.add(it.next());
}
}
}
for (Cell c : set)
new CellChangeJobs.RenameCell(c, newName, newGroupCell);
}
public static void renameCellGroupInJob(Cell.CellGroup cellGroup, String newName)
{
new CellChangeJobs.RenameCellGroup(cellGroup.getCells().next(), newName);
}
/****************************** SHOW CELLS GRAPHICALLY ******************************/
/**
* Method to graph the cells, starting from the current cell.
*/
public static void graphCellsFromCell()
{
Cell top = WindowFrame.needCurCell();
if (top == null) return;
new CellChangeJobs.GraphCells(top);
}
/**
* Method to graph all cells in the current Library.
*/
public static void graphCellsInLibrary()
{
new CellChangeJobs.GraphCells(null);
}
/**
* Method to graph all Library dependencies.
*/
public static void graphLibraries()
{
new CellChangeJobs.GraphLibraries();
}
/****************************** EXTRACT CELL INSTANCES ******************************/
/**
* Method to package the selected objects into a new cell.
*/
public static void packageIntoCell()
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
// get the specified area
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
Cell curCell = wnd.getCell();
if (curCell == null)
{
System.out.println("No cell in this window");
return;
}
Rectangle2D bounds = highlighter.getHighlightedArea(wnd);
if (bounds == null)
{
System.out.println("Must first select circuitry to package");
return;
}
String newCellName = JOptionPane.showInputDialog("New cell name:", curCell.getName());
if (newCellName == null) return;
newCellName += curCell.getView().getAbbreviationExtension();
Set<Geometric> whatToPackage = new HashSet<Geometric>();
List<Geometric> highlighted = highlighter.getHighlightedEObjs(true, true);
for(Geometric geom : highlighted)
{
whatToPackage.add(geom);
if (geom instanceof ArcInst)
{
ArcInst ai = (ArcInst)geom;
whatToPackage.add(ai.getHeadPortInst().getNodeInst());
whatToPackage.add(ai.getTailPortInst().getNodeInst());
}
}
new CellChangeJobs.PackageCell(curCell, whatToPackage, newCellName);
}
/**
* Method to yank the contents of complex node instance "topno" into its
* parent cell.
*/
public static void extractCells(int depth)
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
if (depth < 0)
{
Object obj = JOptionPane.showInputDialog("Number of levels to extract", "1");
if (obj != null) depth = TextUtils.atoi((String)obj);
if (depth <= 0) return;
}
List<NodeInst> selected = MenuCommands.getSelectedNodes();
List<NodeInst> instances = new ArrayList<NodeInst>();
for(NodeInst ni : selected)
{
if (ni.isCellInstance()) instances.add(ni);
}
if (instances.size() == 0)
{
System.out.println("No cell instances are selected...no extraction done");
return;
}
new CellChangeJobs.ExtractCellInstances(cell, instances, depth, User.isExtractCopiesExports(),
User.isIncrementRightmostIndex(), false);
}
/****************************** CLEAN-UP ******************************/
public static void cleanupPinsCommand(boolean everywhere)
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
if (everywhere)
{
boolean cleaned = false;
for(Library lib : Library.getVisibleLibraries())
{
for(Iterator<Cell> it = lib.getCells(); it.hasNext(); )
{
Cell cell = it.next();
if (cleanupCell(cell, false, highlighter)) cleaned = true;
}
}
if (!cleaned) System.out.println("Nothing to clean");
} else
{
// just cleanup the current cell
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
cleanupCell(cell, true, highlighter);
}
}
/**
* Method to clean-up cell "np" as follows:
* remove stranded pins
* collapse redundant (inline) arcs
* highlight zero-size nodes
* removes duplicate arcs
* highlight oversize pins that allow arcs to connect without touching
* move unattached and invisible pins with text in a different location
* resize oversized pins that don't have oversized arcs on them
* Returns true if changes are made.
*/
private static boolean cleanupCell(Cell cell, boolean justThis, Highlighter highlighter)
{
// look for unused pins that can be deleted
Set<NodeInst> pinsToRemove = new HashSet<NodeInst>();
List<CircuitChangeJobs.Reconnect> pinsToPassThrough = CircuitChangeJobs.getPinsToPassThrough(cell);
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.getFunction().isPin()) continue;
// if the pin is an export, save it
if (ni.hasExports()) continue;
// if the pin is not connected or displayed, delete it
if (!ni.hasConnections())
{
// see if the pin has displayable variables on it
boolean hasDisplayable = false;
for(Iterator<Variable> vIt = ni.getVariables(); vIt.hasNext(); )
{
Variable var = vIt.next();
if (var.isDisplay()) { hasDisplayable = true; break; }
}
if (hasDisplayable) continue;
// no displayable variables: delete it
pinsToRemove.add(ni);
continue;
}
}
// look for oversized pins that can be reduced in size
Map<NodeInst,EPoint> pinsToScale = new HashMap<NodeInst,EPoint>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.getFunction().isPin()) continue;
// if the pin is standard size, leave it alone
double overSizeX = ni.getXSize() - ni.getProto().getDefWidth();
if (overSizeX < 0) overSizeX = 0;
double overSizeY = ni.getYSize() - ni.getProto().getDefHeight();
if (overSizeY < 0) overSizeY = 0;
if (overSizeX == 0 && overSizeY == 0) continue;
// all arcs must connect in the pin center
boolean arcsInCenter = true;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
if (ai.getHeadPortInst().getNodeInst() == ni)
{
if (ai.getHeadLocation().getX() != ni.getAnchorCenterX()) { arcsInCenter = false; break; }
if (ai.getHeadLocation().getY() != ni.getAnchorCenterY()) { arcsInCenter = false; break; }
}
if (ai.getTailPortInst().getNodeInst() == ni)
{
if (ai.getTailLocation().getX() != ni.getAnchorCenterX()) { arcsInCenter = false; break; }
if (ai.getTailLocation().getY() != ni.getAnchorCenterY()) { arcsInCenter = false; break; }
}
}
if (!arcsInCenter) continue;
// look for arcs that are oversized
double overSizeArc = 0;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
double overSize = ai.getLambdaBaseWidth() - ai.getProto().getDefaultLambdaBaseWidth();
if (overSize < 0) overSize = 0;
if (overSize > overSizeArc) overSizeArc = overSize;
}
// if an arc covers the pin, leave the pin
if (overSizeArc >= overSizeX && overSizeArc >= overSizeY) continue;
double dSX = 0, dSY = 0;
if (overSizeArc < overSizeX) dSX = overSizeX - overSizeArc;
if (overSizeArc < overSizeY) dSY = overSizeY - overSizeArc;
pinsToScale.put(ni, new EPoint(-dSX, -dSY));
}
// look for pins that are invisible and have text in different location
List<NodeInst> textToMove = new ArrayList<NodeInst>();
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
Point2D pt = ni.invisiblePinWithOffsetText(false);
if (pt != null)
textToMove.add(ni);
}
// highlight oversize pins that allow arcs to connect without touching
int overSizePins = 0;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.getFunction().isPin()) continue;
// make sure all arcs touch each other
boolean nodeIsBad = false;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
Poly poly = ai.makeLambdaPoly(ai.getGridBaseWidth(), Poly.Type.FILLED);
for(Iterator<Connection> oCIt = ni.getConnections(); oCIt.hasNext(); )
{
Connection oCon = oCIt.next();
ArcInst oAi = oCon.getArc();
if (ai.getArcId() <= oAi.getArcId()) continue;
Poly oPoly = oAi.makeLambdaPoly(oAi.getGridBaseWidth(), Poly.Type.FILLED);
double dist = poly.separation(oPoly);
if (dist <= 0) continue;
nodeIsBad = true;
break;
}
if (nodeIsBad) break;
}
if (nodeIsBad)
{
if (justThis)
{
highlighter.addElectricObject(ni, cell);
}
overSizePins++;
}
}
// look for duplicate arcs
Set<ArcInst> arcsToKill = new HashSet<ArcInst>();
for (Iterator<ArcInst> ait = cell.getArcs(); ait.hasNext(); ) {
ArcInst ai = ait.next();
int arcId = ai.getArcId();
if (arcsToKill.contains(ai)) continue;
PortInst pi = ai.getHeadPortInst();
for (Iterator<Connection> it = pi.getConnections(); it.hasNext(); ) {
Connection con = it.next();
ArcInst oAi = con.getArc();
if (oAi.getArcId() >= arcId) continue;
if (ai.getProto() != oAi.getProto()) continue;
int otherEnd = 1 - con.getEndIndex();
PortInst oPi = oAi.getPortInst(otherEnd);
if (oPi != ai.getTailPortInst()) continue;
arcsToKill.add(oAi);
}
}
// // look for duplicate arcs
// HashMap arcsToKill = new HashMap();
// for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
// {
// NodeInst ni = it.next();
// for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
// {
// Connection con = cIt.next();
// ArcInst ai = con.getArc();
// int otherEnd = 1 - con.getEndIndex();
//// int otherEnd = 0;
//// if (ai.getConnection(0) == con) otherEnd = 1;
// boolean foundAnother = false;
// for(Iterator<Connection> oCIt = ni.getConnections(); oCIt.hasNext(); )
// {
// Connection oCon = oCIt.next();
// ArcInst oAi = oCon.getArc();
// if (ai.getArcIndex() <= oAi.getArcIndex()) continue;
// if (con.getPortInst().getPortProto() != oCon.getPortInst().getPortProto()) continue;
// if (ai.getProto() != oAi.getProto()) continue;
// int oOtherEnd = 1 - oCon.getEndIndex();
//// int oOtherEnd = 0;
//// if (oAi.getConnection(0) == oCon) oOtherEnd = 1;
// if (ai.getPortInst(otherEnd).getNodeInst() !=
// oAi.getPortInst(oOtherEnd).getNodeInst()) continue;
// if (ai.getPortInst(otherEnd).getPortProto() !=
// oAi.getPortInst(oOtherEnd).getPortProto()) continue;
//
// // this arc is a duplicate
// arcsToKill.put(oAi, oAi);
// foundAnother = true;
// break;
// }
// if (foundAnother) break;
// }
// }
// now highlight negative or zero-size nodes
int zeroSize = 0, negSize = 0;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getProto() == Generic.tech().cellCenterNode ||
ni.getProto() == Generic.tech().invisiblePinNode ||
ni.getProto() == Generic.tech().universalPinNode ||
ni.getProto() == Generic.tech().essentialBoundsNode) continue;
double sX = ni.getLambdaBaseXSize();
double sY = ni.getLambdaBaseYSize();
if (sX > 0 && sY > 0) continue;
if (sX > 0 || sY > 0 && ni.getProto().getTechnology() == Artwork.tech()) continue;
if (sX == 0 && sY == 0 && ni.getFunction().isPin()) continue;
if (justThis)
{
highlighter.addElectricObject(ni, cell);
}
if (sX < 0 || sY < 0) negSize++; else
zeroSize++;
}
if (pinsToRemove.size() == 0 &&
pinsToPassThrough.size() == 0 &&
pinsToScale.size() == 0 &&
zeroSize == 0 &&
negSize == 0 &&
textToMove.size() == 0 &&
overSizePins == 0 &&
arcsToKill.size() == 0)
{
if (justThis) System.out.println("Nothing to clean");
return false;
}
new CircuitChangeJobs.CleanupChanges(cell, justThis, pinsToRemove, pinsToPassThrough, pinsToScale, textToMove, arcsToKill,
zeroSize, negSize, overSizePins);
return true;
}
/**
* Method to analyze the current cell and show all nonmanhattan geometry.
*/
public static void showNonmanhattanCommand()
{
Cell curCell = WindowFrame.needCurCell();
if (curCell == null) return;
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
// see which cells (in any library) have nonmanhattan stuff
Set<Cell> cellsSeen = new HashSet<Cell>();
for(Iterator<Library> lIt = Library.getLibraries(); lIt.hasNext(); )
{
Library lib = lIt.next();
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
ArcProto ap = ai.getProto();
if (ap.getTechnology() == Generic.tech() || ap.getTechnology() == Artwork.tech() ||
ap.getTechnology() == Schematics.tech()) continue;
Variable var = ai.getVar(ImmutableArcInst.ARC_RADIUS);
if (var != null) cellsSeen.add(cell);
if (ai.getHeadLocation().getX() != ai.getTailLocation().getX() &&
ai.getHeadLocation().getY() != ai.getTailLocation().getY())
cellsSeen.add(cell);
}
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if ((ni.getAngle() % 900) != 0) cellsSeen.add(cell);
}
}
}
// show the nonmanhattan things in the current cell
int i = 0;
for(Iterator<ArcInst> aIt = curCell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
ArcProto ap = ai.getProto();
if (ap.getTechnology() == Generic.tech() || ap.getTechnology() == Artwork.tech() ||
ap.getTechnology() == Schematics.tech()) continue;
boolean nonMan = false;
Variable var = ai.getVar(ImmutableArcInst.ARC_RADIUS);
if (var != null) nonMan = true;
if (ai.getHeadLocation().getX() != ai.getTailLocation().getX() &&
ai.getHeadLocation().getY() != ai.getTailLocation().getY())
nonMan = true;
if (nonMan)
{
if (i == 0) highlighter.clear();
highlighter.addElectricObject(ai, curCell);
i++;
}
}
for(Iterator<NodeInst> nIt = curCell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if ((ni.getAngle() % 900) == 0) continue;
if (i == 0) highlighter.clear();
highlighter.addElectricObject(ni, curCell);
i++;
}
if (i == 0) System.out.println("No nonmanhattan objects in this cell"); else
{
highlighter.finished();
System.out.println(i + " objects are not manhattan in this cell");
}
// tell about other non-manhatten-ness elsewhere
for(Iterator<Library> lIt = Library.getLibraries(); lIt.hasNext(); )
{
Library lib = lIt.next();
if (lib.isHidden()) continue;
int numBad = 0;
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
if (cellsSeen.contains(cell) && cell != curCell) numBad++;
}
if (numBad == 0) continue;
if (lib == Library.getCurrent())
{
int cellsFound = 0;
String infstr = "";
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
if (cell == curCell || !cellsSeen.contains(cell)) continue;
if (cellsFound > 0) infstr += " ";;
infstr += cell.describe(true);
cellsFound++;
}
if (cellsFound == 1)
{
System.out.println("Found nonmanhattan geometry in cell " + infstr);
} else
{
System.out.println("Found nonmanhattan geometry in these cells: " + infstr);
}
} else
{
System.out.println("Found nonmanhattan geometry in " + lib);
}
}
}
/**
* Method to highlight all pure layer nodes in the current cell.
*/
public static void showPureLayerCommand()
{
Cell curCell = WindowFrame.needCurCell();
if (curCell == null) return;
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
// show the pure layer nodes in the current cell
int i = 0;
for(Iterator<NodeInst> nIt = curCell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.getFunction() != PrimitiveNode.Function.NODE) continue;
if (i == 0) highlighter.clear();
highlighter.addElectricObject(ni, curCell);
i++;
}
if (i == 0) System.out.println("No pure layer nodes in this cell"); else
{
highlighter.finished();
System.out.println(i + " pure layer nodes in this cell");
}
}
/**
* Method to shorten all selected arcs.
* Since arcs may connect anywhere inside of the ports on nodes, a port with nonzero area will allow an arc
* to move freely.
* This command shortens selected arcs so that their endpoints arrive at the part of the node that allows the shortest arc.
*/
public static void shortenArcsCommand()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
new CircuitChangeJobs.ShortenArcs(cell, MenuCommands.getSelectedArcs());
}
/**
* Method to show all redundant pure-layer nodes in the Cell.
* It works only for rectangular pure-layer nodes that are inside of other
* (possibly nonrectangular) nodes.
* It works on visible layers only.
*/
public static void showRedundantPureLayerNodes()
{
EditWindow wnd = EditWindow.needCurrent();
if (wnd == null) return;
LayerVisibility lv = wnd.getLayerVisibility();
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
Set<NodeInst> redundantPures = new HashSet<NodeInst>();
for(Iterator<Layer> it = cell.getTechnology().getLayers(); it.hasNext(); )
{
Layer lay = it.next();
if (!lv.isVisible(lay)) continue;
PrimitiveNode pNp = lay.getPureLayerNode();
if (pNp == null) continue;
// find all pure-layer nodes in the cell
List<NodeInst> allPures = new ArrayList<NodeInst>();
Map<NodeInst,Double> pureAreas = new HashMap<NodeInst,Double>();
RTNode root = RTNode.makeTopLevel();
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.getProto() != pNp) continue;
allPures.add(ni);
Point2D [] points = ni.getTrace();
double area;
if (points == null) area = ni.getXSize() * ni.getYSize(); else
{
boolean hasGaps = false;
for(int i=0; i<points.length; i++) if (points[i] == null) { hasGaps = true; break; }
if (hasGaps)
{
// outline has multiple disjoint polygons
area = 0;
int start = 0;
for(int i=0; i<points.length; i++)
{
if (i == points.length-1 || points[i+1] == null)
{
Point2D [] segment = new Point2D[i-start+1];
for(int j=start; j<=i; j++)
segment[j-start] = points[j];
area += GenMath.getAreaOfPoints(segment);
start = i+2;
}
}
} else
{
area = GenMath.getAreaOfPoints(points);
}
}
pureAreas.put(ni, new Double(area));
root = RTNode.linkGeom(null, root, ni);
}
// now find the redundant ones
for(NodeInst ni : allPures)
{
Double nodeArea = pureAreas.get(ni);
Rectangle2D nodeRect = ni.getBounds();
for(RTNode.Search sea = new RTNode.Search(nodeRect, root, false); sea.hasNext(); )
{
NodeInst neighbor = (NodeInst)sea.next();
if (neighbor == ni) continue;
Double neighborArea = pureAreas.get(neighbor);
if (neighborArea.doubleValue() < nodeArea.doubleValue()) continue;
if (redundantPures.contains(neighbor)) continue;
PolyBase [] neighborPolys = neighbor.getProto().getTechnology().getShapeOfNode(neighbor);
PolyBase neighborPoly = neighborPolys[0];
if (neighborPoly.contains(nodeRect))
{
redundantPures.add(ni);
break;
}
}
}
}
// show them
wnd.clearHighlighting();
for(NodeInst ni : redundantPures)
wnd.addElectricObject(ni, cell);
wnd.finishedHighlighting();
System.out.println("Highlighted " + redundantPures.size() + " redundant pure-layer nodes");
}
/**
* Method to return a Rectangle that describes the orthogonal box in this Poly.
* @return the Rectangle that describes this Poly.
* If the Poly is not an orthogonal box, returns null.
* IT IS NOT PERMITTED TO MODIFY THE RETURNED RECTANGLE
* (because it is taken from the internal bounds of the Poly).
*/
public static boolean isBox(Point2D [] points)
{
if (points.length == 4)
{
} else if (points.length == 5)
{
if (points[0].getX() != points[4].getX() || points[0].getY() != points[4].getY()) return false;
} else return false;
// make sure the polygon is rectangular and orthogonal
if (points[0].getX() == points[1].getX() && points[2].getX() == points[3].getX() &&
points[0].getY() == points[3].getY() && points[1].getY() == points[2].getY())
{
return true;
}
if (points[0].getX() == points[3].getX() && points[1].getX() == points[2].getX() &&
points[0].getY() == points[1].getY() && points[2].getY() == points[3].getY())
{
return true;
}
return false;
}
/****************************** MAKE A NEW VERSION OF A CELL ******************************/
public static void newVersionOfCell(Cell cell)
{
// disallow if in Project Management
int status = Project.getCellStatus(cell);
if (status != Project.NOTMANAGED)
{
JOptionPane.showMessageDialog(Main.getCurrentJFrame(),
"This cell is part of a project. To get a new version of it, check it out.", "Cannot Make New Version",
JOptionPane.ERROR_MESSAGE);
return;
}
new CellChangeJobs.NewCellVersion(cell);
}
/****************************** MOVE SELECTED OBJECTS ******************************/
/**
* Method to move the selected geometry by (dX, dY).
*/
public static void manyMove(double dX, double dY)
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf == null) return;
Cell cell = wf.getContent().getCell();
Highlighter highlighter = wf.getContent().getHighlighter();
if (highlighter == null) return;
List<Highlight> highlighted = highlighter.getHighlights();
// prevent mixing cell-center and non-cell-center
int nonCellCenterCount = 0;
Highlight cellCenterHighlight = null;
List<ElectricObject> highlightedEObjs = new ArrayList<ElectricObject>();
for(Highlight h : highlighted)
{
if (!h.isHighlightEOBJ()) continue;
ElectricObject eObj = h.getElectricObject();
highlightedEObjs.add(eObj);
if (eObj instanceof NodeInst)
{
NodeInst ni = (NodeInst)eObj;
if (ni.getProto() == Generic.tech().cellCenterNode) cellCenterHighlight = h; else
nonCellCenterCount++;
} else nonCellCenterCount++;
}
if (cellCenterHighlight != null && nonCellCenterCount != 0)
{
System.out.println("Cannot move the Cell-center along with other objects. Cell-center will not be moved.");
highlighted.remove(cellCenterHighlight);
}
List<DisplayedText> highlightedText = highlighter.getHighlightedText(true);
if (!highlightedEObjs.isEmpty() || !highlightedText.isEmpty())
new CircuitChangeJobs.ManyMove(cell, highlightedEObjs, highlightedText, dX, dY);
}
/****************************** CHANGE CELL EXPANSION ******************************/
/**
* Method to change the expansion of the selected instances.
* @param unExpand true to unexpand the instances (draw them as black boxes),
* false to expand them (draw their contents).
* @param amount the number of levels of hierarchy to expand/unexpand.
* If negative, prompt for an amount.
*/
public static void DoExpandCommands(boolean unExpand, int amount)
{
if (amount < 0)
{
Object obj = JOptionPane.showInputDialog("Number of levels to " + (unExpand ? "unexpand" : "expand"), "1");
if (obj != null) amount = TextUtils.atoi((String)obj);
if (amount <= 0) return;
}
List<NodeInst> list = MenuCommands.getSelectedNodes();
if (list.isEmpty()) return;
Cell cell = list.get(0).getParent();
for(NodeInst ni : list)
{
assert ni.getParent() == cell;
if (!ni.isCellInstance()) continue;
if (!unExpand)
doExpand(cell, ni.getD(), amount, 0);
else if (ni.isExpanded())
doUnExpand(cell, ni.getD(), amount);
}
EditWindow.expansionChanged(cell);
EditWindow.clearSubCellCache();
EditWindow.repaintAllContents();
}
/**
* Method to recursively expand the cell "ni" by "amount" levels.
* "sofar" is the number of levels that this has already been expanded.
*/
private static void doExpand(Cell parent, ImmutableNodeInst n, int amount, int sofar)
{
if (!parent.isExpanded(n.nodeId))
{
// expanded the cell
parent.setExpanded(n.nodeId, true);
// if depth limit reached, quit
if (++sofar >= amount) return;
}
// explore insides of this one
if (!n.isCellInstance()) return;
EDatabase database = parent.getDatabase();
Cell cell = (Cell)n.protoId.inDatabase(database);
for (ImmutableNodeInst subN: cell.backupUnsafe().cellRevision.nodes)
{
if (!subN.isCellInstance()) continue;
// ignore recursive references (showing icon in contents)
Cell subCell = (Cell)subN.protoId.inDatabase(database);
if (subCell.isIconOf(cell)) continue;
doExpand(cell, subN, amount, sofar);
}
}
private static int doUnExpand(Cell parent, ImmutableNodeInst n, int amount)
{
if (!parent.isExpanded(n.nodeId)) return 0;
if (!n.isCellInstance()) return 1;
EDatabase database = parent.getDatabase();
int depth = 0;
Cell cell = (Cell)n.protoId.inDatabase(database);
for (ImmutableNodeInst subN: cell.backupUnsafe().cellRevision.nodes)
{
if (!subN.isCellInstance()) continue;
// ignore recursive references (showing icon in contents)
Cell subCell = (Cell)subN.protoId.inDatabase(database);
if (subCell.isIconOf(cell)) continue;
if (cell.isExpanded(subN.nodeId))
depth = Math.max(depth, doUnExpand(cell, subN, amount));
}
if (depth < amount) {
parent.setExpanded(n.nodeId, false);
}
return depth+1;
}
/****************************** LIBRARY CHANGES ******************************/
/**
* Method to implement the "List Libraries" command.
*/
public static void listLibrariesCommand()
{
System.out.println("----- Libraries: -----");
int k = 0;
for (Library lib : Library.getVisibleLibraries())
{
if (lib.isHidden()) continue;
StringBuffer infstr = new StringBuffer();
infstr.append(lib.getName());
if (lib.isChanged())
{
infstr.append("*");
k++;
}
if (lib.getLibFile() != null)
infstr.append(" (disk file: " + lib.getLibFile() + ")");
System.out.println(infstr.toString());
// see if there are dependencies
Set<String> dummyLibs = new HashSet<String>();
Set<Library> markedLibs = new HashSet<Library>();
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (!ni.isCellInstance()) continue;
Cell subCell = (Cell)ni.getProto();
String pt = subCell.getVarValue(LibraryFiles.IO_TRUE_LIBRARY, String.class);
if (pt != null)
{
dummyLibs.add(pt);
}
markedLibs.add(subCell.getLibrary());
}
}
for(Iterator<Library> lIt = Library.getLibraries(); lIt.hasNext(); )
{
Library oLib = lIt.next();
if (oLib == lib) continue;
if (!markedLibs.contains(oLib)) continue;
System.out.println(" Makes use of cells in " + oLib);
infstr = new StringBuffer();
infstr.append(" These cells make reference to that library:");
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
boolean found = false;
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (!ni.isCellInstance()) continue;
Cell subCell = (Cell)ni.getProto();
if (subCell.getLibrary() == oLib) { found = true; break; }
}
if (found) infstr.append(" " + cell.noLibDescribe());
}
System.out.println(infstr.toString());
}
for(String dummyLibName : dummyLibs)
{
System.out.println(" Has dummy cells that should be in library " + dummyLibName);
infstr = new StringBuffer();
infstr.append(" Instances of these dummy cells are in:");
for(Iterator<Cell> cIt = lib.getCells(); cIt.hasNext(); )
{
Cell cell = cIt.next();
boolean found = false;
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (!ni.isCellInstance()) continue;
Cell subCell = (Cell)ni.getProto();
String libName = subCell.getVarValue(LibraryFiles.IO_TRUE_LIBRARY, String.class);
if (dummyLibName.equals(libName)) { found = true; break; }
}
if (found) infstr.append(" " + cell.noLibDescribe());
}
System.out.println(infstr.toString());
}
}
if (k != 0) System.out.println(" (* means library has changed)");
}
/**
* Method to implement the "Rename Current Technology" command.
*/
public static void renameCurrentTechnology()
{
Technology tech = Technology.getCurrent();
String techName = tech.getTechName();
String val = JOptionPane.showInputDialog("New Name of Technology " + techName + ":", techName);
if (val == null) return;
if (val.equals(techName)) return;
new CircuitChangeJobs.RenameTechnology(tech, val);
}
/**
* Method to implement the "Rename Library" command.
*/
public static void renameLibrary(Library lib)
{
String val = JOptionPane.showInputDialog("New Name of Library:", lib.getName());
if (val == null) return;
new CircuitChangeJobs.RenameLibrary(lib, val);
}
/**
* Method to implement the "Repair Libraries" command.
*/
public static void checkAndRepairCommand(boolean repair)
{
new CircuitChangeJobs.CheckAndRepairJob(repair);
}
/**
* Method to implement the "Find unused library files" command.
*/
public static void findUnusedLibraryFiles()
{
// first make a list of all directories associated with the libraries in memory
Map<String,List<String>> directories = new HashMap<String,List<String>>();
for(Iterator<Library> it = Library.getLibraries(); it.hasNext(); )
{
Library lib = it.next();
if (lib.isHidden()) continue;
if (!lib.isFromDisk()) continue;
String dirName = lib.getLibFile().getFile();
File file = TextUtils.getFile(lib.getLibFile());
if (file == null) continue; // in case of libraries included in jar package
String fileName = file.getName();
// ignore if a library file
URL libFile = LibFile.getLibFile(fileName);
if (libFile != null && libFile.getFile().equals(dirName)) continue;
// crop file name from directory path
int crop = dirName.lastIndexOf(fileName);
if (crop > 0) dirName = dirName.substring(0, crop);
List<String> filesInDir = directories.get(dirName);
if (filesInDir == null)
{
filesInDir = new ArrayList<String>();
directories.put(dirName, filesInDir);
}
filesInDir.add(fileName);
}
if (directories.size() == 0)
{
System.out.println("Before running this command, you must read some libraries from disk.");
System.out.println("The command will then examine the disk to see if there are other libraries that were not read in");
return;
}
for(String dirName : directories.keySet())
{
File dirFile = new File(dirName);
boolean firstInDir = true;
if (dirFile.isDirectory())
{
List<String> filesInDir = directories.get(dirName);
String [] files = dirFile.list();
if (files == null) continue;
for(int i=0; i<files.length; i++)
{
String file = files[i].toLowerCase();
if (file.endsWith(".jelib") || file.endsWith(".elib") || file.endsWith(".delib"))
{
if (filesInDir.contains(files[i])) continue;
if (firstInDir) System.out.println("Directory " + dirName + " has these unused library files:");
firstInDir = false;
System.out.println(" " + files[i]);
}
}
}
}
}
/****************************** DELETE UNUSED NODES ******************************/
/**
* Method to remove nodes containing metal layers that have been disabled.
* If library is null, then check all existing libraries
*/
public static void removeUnusedLayers(Library lib)
{
// kick the delete job
// new RemoveUnusedLayers(lib);
}
}