/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Manipulate.java
* Technology Editor, editing technology libraries
* Written by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2005, 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.tecEdit;
import com.sun.electric.database.geometry.EGraphics;
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.Library;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.Xml;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.extract.LayerCoverageTool;
import com.sun.electric.tool.user.Highlighter;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.dialogs.ComponentMenu;
import com.sun.electric.tool.user.dialogs.EDialog;
import com.sun.electric.tool.user.dialogs.PromptAt;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.WindowFrame;
import com.sun.electric.util.TextUtils;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
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 java.util.StringTokenizer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
/**
* This class manipulates technology libraries.
*/
public class Manipulate
{
/**
* Method to update tables to reflect that cell "oldName" is now called "newName".
* If "newName" is not valid, any rule that refers to "oldName" is removed.
*/
public static void renamedCell(String oldName, String newName)
{
// if this is a layer, rename the layer sequence array
if (oldName.startsWith("layer-") && newName.startsWith("layer-"))
{
renameSequence(Info.LAYERSEQUENCE_KEY, oldName.substring(6), newName.substring(6));
}
// if this is an arc, rename the arc sequence array
if (oldName.startsWith("arc-") && newName.startsWith("arc-"))
{
renameSequence(Info.ARCSEQUENCE_KEY, oldName.substring(4), newName.substring(4));
}
// if this is a node, rename the node sequence array
if (oldName.startsWith("node-") && newName.startsWith("node-"))
{
renameSequence(Info.NODESEQUENCE_KEY, oldName.substring(5), newName.substring(5));
}
// // see if there are design rules in the current library
// var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING|VISARRAY, x_("EDTEC_DRC"));
// if (var == NOVARIABLE) return;
//
// // examine the rules and convert the name
// len = getlength(var);
// sa = newstringarray(us_tool.cluster);
// for(i=0; i<len; i++)
// {
// // parse the DRC rule
// str = ((CHAR **)var.addr)[i];
// origstr = str;
// firstkeyword = getkeyword(&str, x_(" "));
// if (firstkeyword == NOSTRING) return;
//
// // pass wide wire limitation through
// if (*firstkeyword == 'l')
// {
// addtostringarray(sa, origstr);
// continue;
// }
//
// // rename nodes in the minimum node size rule
// if (*firstkeyword == 'n')
// {
// if (namesamen(oldName, x_("node-"), 5) == 0 &&
// namesame(&oldName[5], &firstkeyword[1]) == 0)
// {
// // substitute the new name
// if (namesamen(newName, x_("node-"), 5) == 0)
// {
// infstr = initinfstr();
// addstringtoinfstr(infstr, x_("n"));
// addstringtoinfstr(infstr, &newName[5]);
// addstringtoinfstr(infstr, str);
// addtostringarray(sa, returninfstr(infstr));
// }
// continue;
// }
// addtostringarray(sa, origstr);
// continue;
// }
//
// // rename layers in the minimum layer size rule
// if (*firstkeyword == 's')
// {
// valid = TRUE;
// infstr = initinfstr();
// formatinfstr(infstr, x_("%s "), firstkeyword);
// keyword = getkeyword(&str, x_(" "));
// if (keyword == NOSTRING) return;
// if (namesamen(oldName, x_("layer-"), 6) == 0 &&
// namesame(&oldName[6], keyword) == 0)
// {
// if (namesamen(newName, x_("layer-"), 6) != 0) valid = FALSE; else
// addstringtoinfstr(infstr, &newName[6]);
// } else
// addstringtoinfstr(infstr, keyword);
// addstringtoinfstr(infstr, str);
// str = returninfstr(infstr);
// if (valid) addtostringarray(sa, str);
// continue;
// }
//
// // layer width rule: substitute layer names
// infstr = initinfstr();
// formatinfstr(infstr, x_("%s "), firstkeyword);
// valid = TRUE;
//
// // get the first layer name and convert it
// keyword = getkeyword(&str, x_(" "));
// if (keyword == NOSTRING) return;
// if (namesamen(oldName, x_("layer-"), 6) == 0 &&
// namesame(&oldName[6], keyword) == 0)
// {
// // substitute the new name
// if (namesamen(newName, x_("layer-"), 6) != 0) valid = FALSE; else
// addstringtoinfstr(infstr, &newName[6]);
// } else
// addstringtoinfstr(infstr, keyword);
// addtoinfstr(infstr, ' ');
//
// // get the second layer name and convert it
// keyword = getkeyword(&str, x_(" "));
// if (keyword == NOSTRING) return;
// if (namesamen(oldName, x_("layer-"), 6) == 0 &&
// namesame(&oldName[6], keyword) == 0)
// {
// // substitute the new name
// if (namesamen(newName, x_("layer-"), 6) != 0) valid = FALSE; else
// addstringtoinfstr(infstr, &newName[6]);
// } else
// addstringtoinfstr(infstr, keyword);
//
// addstringtoinfstr(infstr, str);
// str = returninfstr(infstr);
// if (valid) addtostringarray(sa, str);
// }
// strings = getstringarray(sa, &count);
// setval((INTBIG)el_curlib, VLIBRARY, x_("EDTEC_DRC"), (INTBIG)strings,
// VSTRING|VISARRAY|(count<<VLENGTHSH));
// killstringarray(sa);
}
/**
* Method called when a cell has been deleted.
*/
public static void deletedCell(Cell np)
{
if (np.getName().startsWith("layer-"))
{
// may have deleted layer cell in technology library
String layerName = np.getName().substring(6);
StringBuffer warning = null;
for(Iterator<Cell> it = np.getLibrary().getCells(); it.hasNext(); )
{
Cell oNp = it.next();
boolean isNode = false;
if (oNp.getName().startsWith("node-")) isNode = true; else
if (!oNp.getName().startsWith("arc-")) continue;
for(Iterator<NodeInst> nIt = oNp.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
Cell varCell = getLayerCell(ni);
// Variable var = ni.getVar(Info.LAYER_KEY);
// if (var == null) continue;
// CellId cID = (CellId)var.getObject();
// Cell varCell = EDatabase.serverDatabase().getCell(cID);
if (varCell == np)
{
if (warning != null) warning.append(","); else
{
warning = new StringBuffer();
warning.append("Warning: layer " + layerName + " is used in");
}
if (isNode) warning.append(" node " + oNp.getName().substring(5)); else
warning.append(" arc " + oNp.getName().substring(4));
break;
}
}
}
if (warning != null) System.out.println(warning.toString());
// see if this layer is mentioned in the design rules
renamedCell(np.getName(), "");
} else if (np.getName().startsWith("node-"))
{
// see if this node is mentioned in the design rules
renamedCell(np.getName(), "");
}
}
/**
* Method to rename the layer/arc/node sequence arrays to account for a name change.
* The sequence array is in variable "varName", and the item has changed from "oldName" to
* "newName".
*/
private static void renameSequence(Variable.Key varName, String oldName, String newName)
{
Library lib = Library.getCurrent();
Variable var = lib.getVar(varName);
if (var == null) return;
String [] strings = (String [])var.getObject();
for(int i=0; i<strings.length; i++)
if (strings[i].equals(oldName)) strings[i] = newName;
lib.newVar(varName, strings);
}
/**
* Method to determine whether it is legal to place an instance in a technology-edit cell.
* @param np the type of node to create.
* @param cell the cell in which to place it.
* @return true if the creation is invalid (and prints an error message).
*/
public static boolean invalidCreation(NodeProto np, Cell cell)
{
// make sure the cell is right
if (!cell.getName().startsWith("node-") && !cell.getName().startsWith("arc-"))
{
System.out.println("Must be editing a node or arc to place geometry");
return true;
}
if (np == Generic.tech().portNode && !cell.getName().startsWith("node-"))
{
System.out.println("Can only place ports in node descriptions");
return true;
}
return false;
}
/**
* Make a new technology-edit cell of a given type.
* @param type 1=layer, 2=arc, 3=node, 4=factors
*/
public static void makeCell(int type)
{
Library lib = Library.getCurrent();
String cellName = null;
switch (type)
{
case 1: // layer
String layerName = JOptionPane.showInputDialog("Name of new layer:", "");
if (layerName == null) return;
cellName = "layer-" + layerName + "{lay}";
break;
case 2: // arc
String arcName = JOptionPane.showInputDialog("Name of new arc:", "");
if (arcName == null) return;
cellName = "arc-" + arcName + "{lay}";
break;
case 3: // node
String nodeName = JOptionPane.showInputDialog("Name of new node:", "");
if (nodeName == null) return;
cellName = "node-" + nodeName + "{lay}";
break;
case 4: // factors
cellName = "factors";
break;
}
// see if the cell exists
Cell cell = lib.findNodeProto(cellName);
if (cell != null)
{
// cell exists: put it in the current window
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf != null) wf.setCellWindow(cell, null);
return;
}
// create the cell
new MakeOneCellJob(lib, cellName, type);
}
/**
* Class to create a single cell in a technology-library.
*/
private static class MakeOneCellJob extends Job
{
private Library lib;
private String name;
private int type;
private Cell newCell;
private MakeOneCellJob(Library lib, String name, int type)
{
super("Make Cell in Technology-Library", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.lib = lib;
this.name = name;
this.type = type;
startJob();
}
public boolean doIt() throws JobException
{
newCell = Cell.makeInstance(lib, name);
if (newCell == null) return false;
newCell.setInTechnologyLibrary();
newCell.setTechnology(Artwork.tech());
// specialty initialization
switch (type)
{
case 1:
LayerInfo li = new LayerInfo();
li.generate(newCell);
break;
case 2:
ArcInfo aIn = new ArcInfo();
aIn.generate(newCell);
break;
case 3:
NodeInfo nIn = new NodeInfo();
nIn.generate(newCell);
break;
}
// show it
fieldVariableChanged("newCell");
return true;
}
public void terminateOK()
{
WindowFrame wf = WindowFrame.getCurrentWindowFrame();
if (wf != null && newCell != null) wf.setCellWindow(newCell, null);
}
}
/**
* Method to complete the creation of a new node in a technology edit cell.
* @param newNi the node that was just created.
*/
public static void completeNodeCreation(NodeInst newNi, Variable v)
{
// postprocessing on the nodes
String portName = null;
if (newNi.getProto() == Generic.tech().portNode)
{
// a port layer
portName = JOptionPane.showInputDialog("Port name:", "");
if (portName == null) return;
}
boolean isHighlight = false;
if (v != null)
{
if (v.getObject() instanceof Integer &&
((Integer)v.getObject()).intValue() == Info.HIGHLIGHTOBJ)
{
isHighlight = true;
}
}
new AddTechEditMarks(newNi, isHighlight, portName);
}
/**
* Class to prepare a NodeInst for technology editing.
* Adds variables to the NodeInst which identify it to the technology editor.
*/
private static class AddTechEditMarks extends Job
{
private NodeInst newNi;
private boolean isHighlight;
private String portName;
private AddTechEditMarks(NodeInst newNi, boolean isHighlight, String portName)
{
super("Prepare node for technology editing", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.newNi = newNi;
this.isHighlight = isHighlight;
this.portName = portName;
startJob();
}
public boolean doIt() throws JobException
{
if (isHighlight)
{
newNi.newVar(Info.OPTION_KEY, new Integer(Info.HIGHLIGHTOBJ));
return true;
}
// set layer information
newNi.newVar(Info.OPTION_KEY, new Integer(Info.LAYERPATCH));
// postprocessing on the nodes
if (newNi.getProto() == Generic.tech().portNode)
{
// a port layer
newNi.newDisplayVar(Info.PORTNAME_KEY, portName);
return true;
}
// a real layer: default to the first one
String [] layerNames = getLayerNameList();
if (layerNames != null && layerNames.length > 0)
{
Cell cell = Library.getCurrent().findNodeProto(layerNames[0]);
if (cell != null)
{
newNi.newVar(Info.LAYER_KEY, cell.getId());
LayerInfo li = LayerInfo.parseCell(cell);
if (li != null)
setPatch(newNi, li.desc);
}
}
return true;
}
}
/**
* Method to reorganize the dependent libraries
*/
public static void editLibraryDependencies()
{
new EditDependentLibraries();
}
/**
* Method to edit the component menu for the technology.
*/
public static void editComponentMenu()
{
// get information about arcs and nodes in the technology being edited
Library [] dependentlibs = Info.getDependentLibraries(Library.getCurrent());
Cell [] layerCells = Info.findCellSequence(dependentlibs, "layer-", Info.LAYERSEQUENCE_KEY);
Cell [] arcCells = Info.findCellSequence(dependentlibs, "arc-", Info.ARCSEQUENCE_KEY);
Cell [] nodeCells = Info.findCellSequence(dependentlibs, "node-", Info.NODESEQUENCE_KEY);
// get the XML string describing the component menu
String compMenuXML;
Variable var = Library.getCurrent().getVar(Info.COMPMENU_KEY);
if (var == null)
{
// construct a default component menu
List<Object> things = new ArrayList<Object>();
// add in arcs
for(int i=0; i<arcCells.length; i++)
{
String arcName = arcCells[i].getName().substring(4);
Xml.ArcProto curArc = new Xml.ArcProto();
curArc.name = arcName;
things.add(curArc);
}
// add in nodes
for(int i=0; i<nodeCells.length; i++)
{
Cell np = nodeCells[i];
NodeInfo nIn = NodeInfo.parseCell(np);
if (nIn.func == PrimitiveNode.Function.NODE) continue;
String nodeName = nodeCells[i].getName().substring(5);
Xml.PrimitiveNode curNode = new Xml.PrimitiveNode();
curNode.name = nodeName;
curNode.function = nIn.func;
things.add(curNode);
}
// add in special menu entries
things.add(Technology.SPECIALMENUPURE);
things.add(Technology.SPECIALMENUMISC);
things.add(Technology.SPECIALMENUCELL);
// construct the menu information
int columns = (things.size()+13) / 14;
Xml.MenuPalette xmp = new Xml.MenuPalette();
xmp.numColumns = columns;
xmp.menuBoxes = new ArrayList<List<?>>();
for(Object item : things)
{
if (item instanceof List) xmp.menuBoxes.add((List)item); else
{
List<Object> subList = new ArrayList<Object>();
subList.add(item);
xmp.menuBoxes.add(subList);
}
}
compMenuXML = xmp.writeXml();
} else
{
compMenuXML = (String)var.getObject();
}
List<Xml.PrimitiveNode> pureLayerNodes = new ArrayList<Xml.PrimitiveNode>();
for(int i=0; i<layerCells.length; i++)
{
Xml.PrimitiveNode pln = new Xml.PrimitiveNode();
pln.name = "node-" + layerCells[i].getName();
pureLayerNodes.add(pln);
}
List<Xml.PrimitiveNodeGroup> nodeGroups = new ArrayList<Xml.PrimitiveNodeGroup>();
for(int i=0; i<nodeCells.length; i++)
{
Xml.PrimitiveNodeGroup ng = new Xml.PrimitiveNodeGroup();
Xml.PrimitiveNode n = new Xml.PrimitiveNode();
ng.nodes.add(n);
n.name = nodeCells[i].getName().substring(5);
NodeInfo nIn = NodeInfo.parseCell(nodeCells[i]);
n.function = nIn.func;
nodeGroups.add(ng);
}
List<Xml.ArcProto> arcs = new ArrayList<Xml.ArcProto>();
for(int i=0; i<arcCells.length; i++)
{
Xml.ArcProto xap = new Xml.ArcProto();
xap.name = arcCells[i].getName().substring(4);
arcs.add(xap);
}
Xml.MenuPalette xmp = Xml.parseComponentMenuXMLTechEdit(compMenuXML, nodeGroups, arcs, pureLayerNodes);
ComponentMenu.showComponentMenuDialog(Library.getCurrent().getName(), xmp, nodeGroups, arcs);
}
/**
* This class displays a dialog for editing library dependencies.
*/
private static class EditDependentLibraries extends EDialog
{
private JList allLibsList, depLibsList;
private DefaultListModel allLibsModel, depLibsModel;
private JTextField libToAdd;
/** Creates new form edit library dependencies */
private EditDependentLibraries()
{
super(null, true);
initComponents();
setVisible(true);
}
protected void escapePressed() { exit(false); }
// Call this method when the user clicks the OK button
private void exit(boolean goodButton)
{
if (goodButton)
{
int numDeps = depLibsModel.size();
String [] depLibs = new String[numDeps];
for(int i=0; i<numDeps; i++)
depLibs[i] = (String)depLibsModel.get(i);
new ModifyDependenciesJob(depLibs);
}
setVisible(false);
dispose();
}
/**
* Class for saving library dependencies.
*/
private static class ModifyDependenciesJob extends Job
{
private String [] depLibs;
private ModifyDependenciesJob(String [] depLibs)
{
super("Modify Library Dependencies", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.depLibs = depLibs;
startJob();
}
public boolean doIt() throws JobException
{
Library lib = Library.getCurrent();
if (depLibs.length == 0)
{
if (lib.getVar(Info.DEPENDENTLIB_KEY) != null)
lib.delVar(Info.DEPENDENTLIB_KEY);
} else
{
lib.newVar(Info.DEPENDENTLIB_KEY, depLibs);
}
return true;
}
}
private void removeLib()
{
int index = depLibsList.getSelectedIndex();
if (index < 0) return;
depLibsModel.remove(index);
}
private void addLib()
{
String value = (String)allLibsList.getSelectedValue();
String specialLib = libToAdd.getText();
if (specialLib.length() > 0)
{
value = specialLib;
libToAdd.setText("");
}
if (value == null) return;
for(int i=0; i<depLibsModel.size(); i++)
{
String depLib = (String)depLibsModel.get(i);
if (depLib.equals(value)) return;
}
depLibsModel.addElement(value);
}
private void initComponents()
{
getContentPane().setLayout(new GridBagLayout());
setTitle("Dependent Library Selection");
setName("");
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent evt) { exit(false); }
});
// left column
JLabel lab1 = new JLabel("Dependent Libraries:");
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab1, gbc);
JScrollPane depLibsPane = new JScrollPane();
depLibsModel = new DefaultListModel();
depLibsList = new JList(depLibsModel);
depLibsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
depLibsPane.setViewportView(depLibsList);
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.gridheight = 4;
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(depLibsPane, gbc);
depLibsModel.clear();
Library [] libs = Info.getDependentLibraries(Library.getCurrent());
for(int i=0; i<libs.length; i++)
{
if (libs[i] == Library.getCurrent()) continue;
depLibsModel.addElement(libs[i].getName());
}
JLabel lab2 = new JLabel("Current: " + Library.getCurrent().getName());
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 5;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab2, gbc);
JLabel lab3 = new JLabel("Libraries are examined from bottom up");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 6;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab3, gbc);
// center column
JButton remove = new JButton("Remove");
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(remove, gbc);
remove.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { removeLib(); }
});
JButton add = new JButton("<< Add");
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 2;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(add, gbc);
add.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { addLib(); }
});
// right column
JLabel lab4 = new JLabel("All Libraries:");
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab4, gbc);
JScrollPane allLibsPane = new JScrollPane();
allLibsModel = new DefaultListModel();
allLibsList = new JList(allLibsModel);
allLibsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
allLibsPane.setViewportView(allLibsList);
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 1;
gbc.gridheight = 2;
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(allLibsPane, gbc);
allLibsModel.clear();
for(Library lib : Library.getVisibleLibraries())
{
allLibsModel.addElement(lib.getName());
}
JLabel lab5 = new JLabel("Library (if not in list):");
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 3;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(lab5, gbc);
libToAdd = new JTextField("");
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 4;
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(libToAdd, gbc);
// OK and Cancel
JButton cancel = new JButton("Cancel");
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 6;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(cancel, gbc);
cancel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(false); }
});
JButton ok = new JButton("OK");
getRootPane().setDefaultButton(ok);
gbc = new GridBagConstraints();
gbc.gridx = 2;
gbc.gridy = 6;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(ok, gbc);
ok.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(true); }
});
pack();
}
}
/**
* Method to highlight information about all layers (or ports if "doPorts" is true)
*/
public static void identifyLayers(boolean doPorts)
{
EditWindow wnd = EditWindow.getCurrent();
Cell np = WindowFrame.needCurCell();
if (wnd == null || np == null) return;
if (doPorts)
{
if (!np.getName().startsWith("node-"))
{
System.out.println("Must be editing a node to identify ports");
return;
}
} else
{
if (!np.getName().startsWith("node-") && !np.getName().startsWith("arc-"))
{
System.out.println("Must be editing a node or arc to identify layers");
return;
}
}
// get examples
List<Example> neList = null;
TechConversionResult tcr = new TechConversionResult();
if (np.getName().startsWith("node-"))
neList = Example.getExamples(np, true, tcr, null); else
neList = Example.getExamples(np, false, tcr, null);
if (tcr.failed()) tcr.showError();
if (neList == null || neList.size() == 0) return;
Example firstEx = neList.get(0);
// count the number of appropriate samples in the main example
int total = 0;
for(Sample ns : firstEx.samples)
{
if (!doPorts)
{
if (ns.layer != Generic.tech().portNode) total++;
} else
{
if (ns.layer == Generic.tech().portNode) total++;
}
}
if (total == 0)
{
System.out.println("There are no " + (doPorts ? "ports" : "layers") + " to identify");
return;
}
// make arrays for position and association
double [] xPos = new double[total];
double [] yPos = new double[total];
Poly.Type [] style = new Poly.Type[total];
Sample [] whichSam = new Sample[total];
// fill in label positions
int qTotal = (total+3) / 4;
Rectangle2D screen = wnd.getBoundsInWindow();
double ySep = screen.getHeight() / qTotal;
double xSep = screen.getWidth() / qTotal;
double indent = screen.getHeight() / 15;
for(int i=0; i<qTotal; i++)
{
// label on the left side
xPos[i] = screen.getMinX() + indent;
yPos[i] = screen.getMinY() + ySep * i + ySep/2;
style[i] = Poly.Type.TEXTLEFT;
if (i+qTotal < total)
{
// label on the top side
xPos[i+qTotal] = screen.getMinX() + xSep * i + xSep/2;
yPos[i+qTotal] = screen.getMaxY() - indent;
style[i+qTotal] = Poly.Type.TEXTTOP;
}
if (i+qTotal*2 < total)
{
// label on the right side
xPos[i+qTotal*2] = screen.getMaxX() - indent;
yPos[i+qTotal*2] = screen.getMinY() + ySep * i + ySep/2;
style[i+qTotal*2] = Poly.Type.TEXTRIGHT;
}
if (i+qTotal*3 < total)
{
// label on the bottom side
xPos[i+qTotal*3] = screen.getMinX() + xSep * i + xSep/2;
yPos[i+qTotal*3] = screen.getMinY() + indent;
style[i+qTotal*3] = Poly.Type.TEXTBOT;
}
}
// fill in sample associations
int k = 0;
for(Sample ns : firstEx.samples)
{
if (!doPorts)
{
if (ns.layer != Generic.tech().portNode) whichSam[k++] = ns;
} else
{
if (ns.layer == Generic.tech().portNode) whichSam[k++] = ns;
}
}
// rotate through all configurations, finding least distance
double bestDist = Double.MAX_VALUE;
int bestRot = 0;
for(int i=0; i<total; i++)
{
// find distance from each label to its sample center
double dist = 0;
for(int j=0; j<total; j++)
dist += new Point2D.Double(xPos[j], yPos[j]).distance(new Point2D.Double(whichSam[j].xPos, whichSam[j].yPos));
if (dist < bestDist)
{
bestDist = dist;
bestRot = i;
}
// rotate the samples
Sample ns = whichSam[0];
for(int j=1; j<total; j++) whichSam[j-1] = whichSam[j];
whichSam[total-1] = ns;
}
// rotate back to the best orientation
for(int i=0; i<bestRot; i++)
{
Sample ns = whichSam[0];
for(int j=1; j<total; j++) whichSam[j-1] = whichSam[j];
whichSam[total-1] = ns;
}
// draw the highlighting
Highlighter highlighter = wnd.getHighlighter();
highlighter.clear();
for(int i=0; i<total; i++)
{
Sample ns = whichSam[i];
String msg = null;
if (ns.layer == null)
{
msg = "HIGHLIGHT";
} else if (ns.layer == Generic.tech().cellCenterNode)
{
msg = "GRAB";
} else if (ns.layer == Generic.tech().portNode)
{
msg = Info.getPortName(ns.node);
if (msg == null) msg = "?";
} else msg = ns.layer.getName().substring(6);
Point2D curPt = new Point2D.Double(xPos[i], yPos[i]);
highlighter.addMessage(np, msg, curPt);
Rectangle2D nodeBounds = ns.node.getBaseShape().getBounds2D();
Point2D other = null;
if (style[i] == Poly.Type.TEXTLEFT)
{
other = new Point2D.Double(nodeBounds.getMinX(), nodeBounds.getCenterY());
} else if (style[i] == Poly.Type.TEXTRIGHT)
{
other = new Point2D.Double(nodeBounds.getMaxX(), nodeBounds.getCenterY());
} else if (style[i] == Poly.Type.TEXTTOP)
{
other = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getMaxY());
} else if (style[i] == Poly.Type.TEXTBOT)
{
other = new Point2D.Double(nodeBounds.getCenterX(), nodeBounds.getMinY());
}
highlighter.addLine(curPt, other, np);
}
highlighter.finished();
}
/**
* Method to return information about a given object.
*/
public static String describeNodeMeaning(Geometric geom)
{
if (geom instanceof ArcInst)
{
// describe currently highlighted arc
ArcInst ai = (ArcInst)geom;
if (ai.getProto() != Generic.tech().universal_arc)
return "This is an unimportant " + ai.getProto();
if (ai.getHeadPortInst().getNodeInst().getProto() != Generic.tech().portNode ||
ai.getTailPortInst().getNodeInst().getProto() != Generic.tech().portNode)
return "This arc makes an unimportant connection";
String pt1 = Info.getPortName(ai.getHeadPortInst().getNodeInst());
String pt2 = Info.getPortName(ai.getTailPortInst().getNodeInst());
if (pt1 == null || pt2 == null)
return "This arc connects two port objects";
return "This arc connects ports '" + pt1 + "' and '" + pt2 + "'";
}
NodeInst ni = (NodeInst)geom;
Cell cell = ni.getParent();
int opt = getOptionOnNode(ni);
if (opt < 0) return "No relevance";
switch (opt)
{
case Info.TECHSHORTNAME:
return "The technology name";
case Info.TECHSCALE:
return "The technology scale";
case Info.TECHFOUNDRY:
return "The technology's foundry";
case Info.TECHDEFMETALS:
return "The number of metal layers in the technology";
case Info.TECHDESCRIPT:
return "The technology description";
case Info.TECHSPICEMINRES:
return "Minimum resistance of SPICE elements";
case Info.TECHSPICEMINCAP:
return "Minimum capacitance of SPICE elements";
case Info.TECHMAXSERIESRES:
return "Maximum series resistance of SPICE elements";
case Info.TECHGATESHRINK:
return "The shrinkage of gates, in um";
case Info.TECHGATEINCLUDED:
return "Whether gates are included in resistance";
case Info.TECHGROUNDINCLUDED:
return "Whether to include the ground network in parasitics";
case Info.TECHTRANSPCOLORS:
return "The transparent colors";
case Info.LAYER3DHEIGHT:
return "The 3D height of " + cell;
case Info.LAYER3DTHICK:
return "The 3D thickness of " + cell;
case Info.LAYERTRANSPARENCY:
return "The transparency layer of " + cell;
case Info.LAYERCIF:
return "The CIF name of " + cell;
case Info.LAYERCOLOR:
return "The color of " + cell;
case Info.LAYERLETTERS:
return "The unique letter for " + cell + " (obsolete)";
case Info.LAYERDXF:
return "The DXF name(s) of " + cell + " (obsolete)";
case Info.LAYERDRCMINWID:
return "DRC minimum width " + cell + " (obsolete)";
case Info.LAYERFUNCTION:
return "The function of " + cell;
case Info.LAYERGDS:
return "The Calma GDS-II number of " + cell;
case Info.LAYERPATCONT:
return "A stipple-pattern controller";
case Info.LAYERPATTERN:
return "One of the bitmap squares in " + cell;
case Info.LAYERSPICAP:
return "The SPICE capacitance of " + cell;
case Info.LAYERSPIECAP:
return "The SPICE edge capacitance of " + cell;
case Info.LAYERSPIRES:
return "The SPICE resistance of " + cell;
case Info.LAYERSTYLE:
return "The style of " + cell;
case Info.LAYERCOVERAGE:
return "The desired coverage percentage for " + cell;
case Info.ARCFIXANG:
return "Whether " + cell + " is fixed-angle";
case Info.ARCFUNCTION:
return "The function of " + cell;
case Info.ARCINC:
return "The prefered angle increment of " + cell;
case Info.ARCNOEXTEND:
return "The arc extension of " + cell;
case Info.ARCWIPESPINS:
return "Thie arc coverage of " + cell;
case Info.ARCANTENNARATIO:
return "The maximum antenna ratio for " + cell;
case Info.ARCWIDTHOFFSET:
return "The ELIB width offset for " + cell;
case Info.NODEFUNCTION:
return "The function of " + cell;
case Info.NODELOCKABLE:
return "Whether " + cell + " can be locked (used in array technologies)";
case Info.NODEMULTICUT:
return "The separation between multiple contact cuts in " + cell + " (obsolete)";
case Info.NODESERPENTINE:
return "Whether " + cell + " is a serpentine transistor";
case Info.NODESQUARE:
return "Whether " + cell + " is square";
case Info.NODEWIPES:
return "Whether " + cell + " disappears when conencted to one or two arcs";
case Info.NODESPICETEMPLATE:
return "Spice template for " + cell;
case Info.CENTEROBJ:
return "The grab point of " + cell;
case Info.LAYERPATCH:
case Info.HIGHLIGHTOBJ:
Cell np = getLayerCell(ni);
if (np == null) return "Highlight box";
String msg = "Layer '" + np.getName().substring(6) + "'";
Variable var = ni.getVar(Info.MINSIZEBOX_KEY);
if (var != null) msg += " (at minimum size)";
return msg;
case Info.PORTOBJ:
String pt = Info.getPortName(ni);
if (pt == null) return "Unnamed port";
return "Port '" + pt + "'";
}
return "Unknown information";
}
/**
* Method to obtain the layer associated with node "ni". Returns 0 if the layer is not
* there or invalid. Returns null if this is the highlight layer.
*/
static Cell getLayerCell(NodeInst ni)
{
Variable var = ni.getVar(Info.LAYER_KEY);
if (var == null) return null;
CellId cID = (CellId)var.getObject();
Cell clientCell = EDatabase.clientDatabase().getCell(cID);
Cell cell = EDatabase.serverDatabase().getCell(cID);
if (clientCell != null || cell != null)
{
// validate the reference
for(Iterator<Cell> it = ni.getParent().getLibrary().getCells(); it.hasNext(); )
{
Cell oCell = it.next();
if (oCell == cell || oCell == clientCell) return oCell;
}
}
System.out.println("Layer " + cID.cellName + " not found");
return null;
}
/**
* Method for modifying the selected object. If two are selected, connect them.
*/
public static void modifyObject(EditWindow wnd, NodeInst ni, int opt)
{
// handle other cases
switch (opt)
{
case Info.TECHSHORTNAME: modTechShortName(wnd, ni); break;
case Info.TECHSCALE: modTechScale(wnd, ni); break;
case Info.TECHFOUNDRY: modTechFoundry(wnd, ni); break;
case Info.TECHDEFMETALS: modTechNumMetals(wnd, ni); break;
case Info.TECHDESCRIPT: modTechDescription(wnd, ni); break;
case Info.TECHSPICEMINRES: modTechMinResistance(wnd, ni); break;
case Info.TECHSPICEMINCAP: modTechMinCapacitance(wnd, ni); break;
case Info.TECHMAXSERIESRES: modTechMaxSeriesResistance(wnd, ni);break;
case Info.TECHGATESHRINK: modTechGateShrinkage(wnd, ni); break;
case Info.TECHGATEINCLUDED: modTechGateIncluded(wnd, ni); break;
case Info.TECHGROUNDINCLUDED:modTechGroundIncluded(wnd, ni); break;
case Info.TECHTRANSPCOLORS: modTechTransparentColors(wnd, ni); break;
case Info.LAYERFUNCTION: modLayerFunction(wnd, ni); break;
case Info.LAYERCOLOR: modLayerColor(wnd, ni); break;
case Info.LAYERTRANSPARENCY: modLayerTransparency(wnd, ni); break;
case Info.LAYERSTYLE: modLayerStyle(wnd, ni); break;
case Info.LAYERCIF: modLayerCIF(wnd, ni); break;
case Info.LAYERGDS: modLayerGDS(wnd, ni); break;
case Info.LAYERSPIRES: modLayerResistance(wnd, ni); break;
case Info.LAYERSPICAP: modLayerCapacitance(wnd, ni); break;
case Info.LAYERSPIECAP: modLayerEdgeCapacitance(wnd, ni); break;
case Info.LAYER3DHEIGHT: modLayerHeight(wnd, ni); break;
case Info.LAYER3DTHICK: modLayerThickness(wnd, ni); break;
case Info.LAYERPATTERN: modLayerPattern(wnd, ni); break;
case Info.LAYERPATCONT: doPatternControl(wnd, ni, 0); break;
case Info.LAYERPATCLEAR: doPatternControl(wnd, ni, 1); break;
case Info.LAYERPATINVERT: doPatternControl(wnd, ni, 2); break;
case Info.LAYERPATCOPY: doPatternControl(wnd, ni, 3); break;
case Info.LAYERPATPASTE: doPatternControl(wnd, ni, 4); break;
case Info.LAYERPATCH: modLayerPatch(wnd, ni); break;
case Info.LAYERCOVERAGE: modLayerCoverage(wnd, ni); break;
case Info.ARCFIXANG: modArcFixAng(wnd, ni); break;
case Info.ARCFUNCTION: modArcFunction(wnd, ni); break;
case Info.ARCINC: modArcAngInc(wnd, ni); break;
case Info.ARCNOEXTEND: modArcExtension(wnd, ni); break;
case Info.ARCWIPESPINS: modArcWipes(wnd, ni); break;
case Info.ARCANTENNARATIO: modArcAntennaRatio(wnd, ni); break;
case Info.ARCWIDTHOFFSET: modArcWidthOffset(wnd, ni); break;
case Info.NODEFUNCTION: modNodeFunction(wnd, ni); break;
case Info.NODELOCKABLE: modNodeLockability(wnd, ni); break;
case Info.NODESERPENTINE: modNodeSerpentine(wnd, ni); break;
case Info.NODESQUARE: modNodeSquare(wnd, ni); break;
case Info.NODEWIPES: modNodeWipes(wnd, ni); break;
case Info.NODESPICETEMPLATE: modNodeSpiceTemplate(wnd, ni); break;
case Info.PORTOBJ: modPort(wnd, ni); break;
case Info.HIGHLIGHTOBJ:
System.out.println("Cannot modify highlight boxes");
break;
default:
System.out.println("Cannot modify this object");
break;
}
}
/***************************** OBJECT MODIFICATION *****************************/
private static void modTechMinResistance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Minimum Resistance",
"Minimum resistance (for parasitics):", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Minimum Resistance: " + newUnit);
}
private static void modTechMinCapacitance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Minimum Capacitance",
"Minimum capacitance (for parasitics):", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Minimum Capacitance: " + newUnit);
}
private static void modTechMaxSeriesResistance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Maximum Series Resistance",
"Maximum Series Resistance (for parasitics):", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Maximum Series Resistance: " + newUnit);
}
private static void modArcAntennaRatio(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Antenna Ratio",
"Maximum antenna ratio for this layer:", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Antenna Ratio: " + newUnit);
}
private static void modArcWidthOffset(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Width Offset",
"ELIB width offset for this arc:", initialMsg);
if (newUnit != null) new SetTextJob(ni, "ELIB width offset: " + newUnit);
}
private static void modLayerCoverage(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Coverage Percent",
"Desired coverage percentage:", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Coverage percent: " + newUnit);
}
private static void modTechGateShrinkage(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Change Gate Shrinkage",
"Gate shrinkage (in um):", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Gate Shrinkage: " + newUnit);
}
private static void modTechGateIncluded(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Change Whether Gate is Included in Resistance",
"Should the gate be included in resistance?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Gates Included in Resistance: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modTechGroundIncluded(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Change Whether parasitics include the ground network",
"Should parasitics include the ground network?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Parasitics Includes Ground: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modTechTransparentColors(EditWindow wnd, NodeInst ni)
{
Color [] colors = GeneralInfo.getTransparentColors(ni);
if (colors == null) return;
for(;;)
{
PromptAt.Field [][] fields = new PromptAt.Field[colors.length+1][2];
for(int i=0; i<colors.length; i++)
{
fields[i][0] = new PromptAt.Field("Transparent layer " + (i+1) + ":", colors[i]);
JButton but = new JButton("Remove");
fields[i][1] = new PromptAt.Field(""+(i+1), but);
}
JButton addBut = new JButton("Add");
fields[colors.length][0] = new PromptAt.Field("add", addBut);
String choice = PromptAt.showPromptAt(wnd, ni, "Change Transparent Colors", fields);
if (choice == null) return;
if (choice.length() == 0)
{
// done
for(int i=0; i<colors.length; i++)
colors[i] = (Color)fields[i][0].getFinal();
new SetTransparentColorJob(ni, GeneralInfo.makeTransparentColorsLine(colors));
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
break;
}
if (choice.equals("add"))
{
// add a layer
Color [] newColors = new Color[colors.length+1];
for(int i=0; i<colors.length; i++)
newColors[i] = (Color)fields[i][0].getFinal();
newColors[colors.length] = new Color(128, 128, 128);
colors = newColors;
continue;
}
// a layer was removed
int remove = TextUtils.atoi(choice);
Color [] newColors = new Color[colors.length-1];
int j = 0;
for(int i=0; i<colors.length; i++)
{
if (i+1 == remove) continue;
newColors[j++] = (Color)fields[i][0].getFinal();
}
colors = newColors;
}
}
private static void modLayerHeight(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newHei = PromptAt.showPromptAt(wnd, ni, "Change 3D Height",
"New 3D height (depth) for this layer:", initialMsg);
if (newHei != null) new SetTextJob(ni, "3D Height: " + newHei);
}
private static void modLayerThickness(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newThk = PromptAt.showPromptAt(wnd, ni, "Change 3D Thickness",
"New 3D thickness for this layer:", initialMsg);
if (newThk != null) new SetTextJob(ni, "3D Thickness: " + newThk);
}
private static void modLayerColor(EditWindow wnd, NodeInst ni)
{
String initialString = Info.getValueOnNode(ni);
StringTokenizer st = new StringTokenizer(initialString, ",");
if (st.countTokens() != 5)
{
System.out.println("Color information must have 5 fields, separated by commas");
return;
}
PromptAt.Field [] fields = new PromptAt.Field[3];
int r = TextUtils.atoi(st.nextToken());
int g = TextUtils.atoi(st.nextToken());
int b = TextUtils.atoi(st.nextToken());
fields[0] = new PromptAt.Field("Color:", new Color(r, g, b));
fields[1] = new PromptAt.Field("Opacity (0-1):", st.nextToken());
fields[2] = new PromptAt.Field("Foreground:", new String [] {"on", "off"}, st.nextToken());
String choice = PromptAt.showPromptAt(wnd, ni, "Change Color", fields);
if (choice == null) return;
Color col = (Color)fields[0].getFinal();
r = col.getRed();
g = col.getGreen();
b = col.getBlue();
double o = TextUtils.atof((String)fields[1].getFinal());
String oo = (String)fields[2].getFinal();
new SetTextJob(ni, "Color: " + r + "," + g + "," + b + ", " + o + "," + oo);
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
}
private static void modLayerTransparency(EditWindow wnd, NodeInst ni)
{
String initialTransLayer = Info.getValueOnNode(ni);
String [] transNames = new String[11];
transNames[0] = "none";
transNames[1] = "layer-1";
transNames[2] = "layer-2";
transNames[3] = "layer-3";
transNames[4] = "layer-4";
transNames[5] = "layer-5";
transNames[6] = "layer-6";
transNames[7] = "layer-7";
transNames[8] = "layer-8";
transNames[9] = "layer-9";
transNames[10] = "layer-10";
String choice = PromptAt.showPromptAt(wnd, ni, "Change Transparent Layer",
"New transparent layer number for this layer:", initialTransLayer, transNames);
if (choice == null) return;
new SetTextJob(ni, "Transparency: " + choice);
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
}
private static final String PRINTSOLID = "PRINTER: Solid";
private static final String PRINTPATTERNED = "PRINTER: Patterned";
private static void modLayerStyle(EditWindow wnd, NodeInst ni)
{
String initialStyleName = Info.getValueOnNode(ni);
String printerPart;
int commaPos = initialStyleName.indexOf(',');
if (commaPos < 0) printerPart = ""; else
{
printerPart = initialStyleName.substring(commaPos);
initialStyleName = initialStyleName.substring(0, commaPos);
}
List<EGraphics.Outline> outlines = EGraphics.Outline.getOutlines();
String [] styleNames = new String[outlines.size()+3];
styleNames[0] = "Solid";
int i = 1;
for(EGraphics.Outline o : outlines)
{
styleNames[i++] = "Patterned/Outline=" + o.getName();
}
styleNames[i++] = PRINTSOLID;
styleNames[i++] = PRINTPATTERNED;
String choice = PromptAt.showPromptAt(wnd, ni, "Change Layer Drawing Style",
"New drawing style for this layer:", initialStyleName, styleNames);
if (choice == null) return;
if (choice.equals(PRINTSOLID))
{
choice = initialStyleName + ",PrintSolid";
} else if (choice.equals(PRINTPATTERNED))
{
choice = initialStyleName;
} else choice += printerPart;
new SetTextJob(ni, "Style: " + choice);
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
}
private static void modLayerCIF(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newCIF = PromptAt.showPromptAt(wnd, ni, "Change CIF layer name", "New CIF symbol for this layer:", initialMsg);
if (newCIF != null) new SetTextJob(ni, "CIF Layer: " + newCIF);
}
private static void modLayerGDS(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newGDS = PromptAt.showPromptAt(wnd, ni, "Change GDS layer name", "New GDS symbol for this layer:", initialMsg);
if (newGDS != null) new SetTextJob(ni, "GDS-II Layer: " + newGDS);
}
private static void modLayerResistance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newRes = PromptAt.showPromptAt(wnd, ni, "Change SPICE Layer Resistance",
"New SPICE resistance for this layer:", initialMsg);
if (newRes != null) new SetTextJob(ni, "SPICE Resistance: " + newRes);
}
private static void modLayerCapacitance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newCap = PromptAt.showPromptAt(wnd, ni, "Change SPICE Layer Capacitance",
"New SPICE capacitance for this layer:", initialMsg);
if (newCap != null) new SetTextJob(ni, "SPICE Capacitance: " + newCap);
}
private static void modLayerEdgeCapacitance(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newCap = PromptAt.showPromptAt(wnd, ni, "Change SPICE Layer Edge Capacitance",
"New SPICE edge capacitance for this layer:", initialMsg);
if (newCap != null) new SetTextJob(ni, "SPICE Edge Capacitance: " + newCap);
}
private static void modLayerFunction(EditWindow wnd, NodeInst ni)
{
String initialFuncName = Info.getValueOnNode(ni);
int commaPos = initialFuncName.indexOf(',');
if (commaPos >= 0) initialFuncName = initialFuncName.substring(0, commaPos);
// make a list of all layer functions and extras
List<Layer.Function> funs = Layer.Function.getFunctions();
int [] extraBits = Layer.Function.getFunctionExtras();
String [] functionNames = new String[funs.size() + extraBits.length];
int j = 0;
for(Layer.Function fun : funs)
{
functionNames[j++] = fun.toString();
}
for(int i=0; i<extraBits.length; i++)
functionNames[j++] = Layer.Function.getExtraName(extraBits[i]);
// prompt for a new layer function
String choice = PromptAt.showPromptAt(wnd, ni, "Change Layer Function", "New function for this layer:", initialFuncName, functionNames);
if (choice == null) return;
// see if the choice is an extra
int thisExtraBit = -1;
for(int i=0; i<extraBits.length; i++)
{
if (choice.equals(Layer.Function.getExtraName(extraBits[i]))) { thisExtraBit = extraBits[i]; break; }
}
LayerInfo li = LayerInfo.parseCell(ni.getParent());
if (li == null) return;
if (thisExtraBit > 0)
{
// adding (or removing) an extra bit
if ((li.funExtra & thisExtraBit) != 0) li.funExtra &= ~thisExtraBit; else
li.funExtra |= thisExtraBit;
} else
{
li.funExtra = 0;
for(Layer.Function fun : funs)
{
if (fun.toString().equalsIgnoreCase(choice))
{
li.fun = fun;
break;
}
}
}
new SetTextJob(ni, "Function: " + LayerInfo.makeLayerFunctionName(li.fun, li.funExtra));
}
private static int [] copiedPattern = null;
private static void doPatternControl(EditWindow wnd, NodeInst ni, int forced)
{
if (forced == 0)
{
String [] operationNames = new String[4];
operationNames[0] = "Clear Pattern";
operationNames[1] = "Invert Pattern";
operationNames[2] = "Copy Pattern";
operationNames[3] = "Paste Pattern";
String choice = PromptAt.showPromptAt(wnd, ni, "Pattern Operations", null, "", operationNames);
if (choice == null) return;
if (choice.equals("Clear Pattern")) forced = 1; else
if (choice.equals("Invert Pattern")) forced = 2; else
if (choice.equals("Copy Pattern")) forced = 3; else
if (choice.equals("Paste Pattern")) forced = 4;
}
switch (forced)
{
case 1: // clear pattern
for(Iterator<NodeInst> it = ni.getParent().getNodes(); it.hasNext(); )
{
NodeInst pni = it.next();
int opt = getOptionOnNode(pni);
if (opt != Info.LAYERPATTERN) continue;
int color = getLayerColor(pni);
if (color != 0)
new SetLayerPatternJob(pni, 0);
}
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
break;
case 2: // invert pattern
for(Iterator<NodeInst> it = ni.getParent().getNodes(); it.hasNext(); )
{
NodeInst pni = it.next();
int opt = getOptionOnNode(pni);
if (opt != Info.LAYERPATTERN) continue;
int color = getLayerColor(pni);
new SetLayerPatternJob(pni, ~color);
}
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
break;
case 3: // copy pattern
LayerInfo li = LayerInfo.parseCell(ni.getParent());
if (li == null) return;
copiedPattern = li.desc.getPattern();
break;
case 4: // paste pattern
if (copiedPattern == null) return;
setLayerPattern(ni.getParent(), copiedPattern);
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
break;
}
}
/**
* Method to return the color in layer-pattern node "ni" (off is 0, on is 0xFFFF).
*/
private static int getLayerColor(NodeInst ni)
{
if (ni.getProto() == Artwork.tech().boxNode) return 0;
if (ni.getProto() != Artwork.tech().filledBoxNode) return 0;
Variable var = ni.getVar(Artwork.ART_PATTERN);
if (var == null) return 0xFFFF;
return ((Short[])var.getObject())[0].intValue();
}
/**
* Class to create a technology-library from a technology.
*/
private static class SetLayerPatternJob extends Job
{
private NodeInst ni;
private int color;
private SetLayerPatternJob(NodeInst ni, int color)
{
super("Change Pattern In Layer", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.color = color;
startJob();
}
public boolean doIt() throws JobException
{
if (ni.getProto() == Artwork.tech().boxNode)
{
if (color == 0) return true;
ni.replace(Artwork.tech().filledBoxNode, false, false);
} else if (ni.getProto() == Artwork.tech().filledBoxNode)
{
Short [] col = new Short[16];
for(int i=0; i<16; i++) col[i] = new Short((short)color);
ni.newVar(Artwork.ART_PATTERN, col);
}
return true;
}
}
/**
* Method to toggle the color of layer-pattern node "ni" (called when the user does a
* "technology edit" click on the node).
*/
private static void modLayerPattern(EditWindow wnd, NodeInst ni)
{
int color = getLayerColor(ni);
new SetLayerPatternJob(ni, ~color);
Highlighter h = wnd.getHighlighter();
h.clear();
h.addElectricObject(ni, ni.getParent());
// redraw the demo layer in this cell
new RedoLayerGraphicsJob(ni.getParent());
}
/**
* Method to get a list of layers in the current library (in the proper order).
* @return an array of strings with the names of the layers.
*/
private static String [] getLayerNameList()
{
Library [] dependentlibs = Info.getDependentLibraries(Library.getCurrent());
Cell [] layerCells = Info.findCellSequence(dependentlibs, "layer-", Info.LAYERSEQUENCE_KEY);
// build and fill array of layers for DRC parsing
String [] layerNames = new String[layerCells.length];
for(int i=0; i<layerCells.length; i++)
layerNames[i] = layerCells[i].getName().substring(6);
return layerNames;
}
/**
* Method to get a list of arcs in the current library (in the proper order).
* @return an array of strings with the names of the arcs.
*/
private static String [] getArcNameList()
{
Library [] dependentlibs = Info.getDependentLibraries(Library.getCurrent());
Cell [] arcCells = Info.findCellSequence(dependentlibs, "arc-", Info.ARCSEQUENCE_KEY);
// build and fill array of layers for DRC parsing
String [] arcNames = new String[arcCells.length];
for(int i=0; i<arcCells.length; i++)
arcNames[i] = arcCells[i].getName().substring(4);
return arcNames;
}
/**
* Method to get a list of arcs in the current library (in the proper order).
* @return an array of strings with the names of the arcs.
*/
private static String [] getNodeNameList()
{
Library [] dependentlibs = Info.getDependentLibraries(Library.getCurrent());
Cell [] nodeCells = Info.findCellSequence(dependentlibs, "node-", Info.NODESEQUENCE_KEY);
// build and fill array of nodes
String [] nodeNames = new String[nodeCells.length];
for(int i=0; i<nodeCells.length; i++)
nodeNames[i] = nodeCells[i].getName().substring(5);
return nodeNames;
}
/**
* Method to modify the layer information in node "ni".
*/
private static void modLayerPatch(EditWindow wnd, NodeInst ni)
{
Library [] dependentlibs = Info.getDependentLibraries(Library.getCurrent());
Cell [] layerCells = Info.findCellSequence(dependentlibs, "layer-", Info.LAYERSEQUENCE_KEY);
if (layerCells == null) return;
String [] options = new String[layerCells.length + 2];
for(int i=0; i<layerCells.length; i++)
options[i] = layerCells[i].getName().substring(6);
options[layerCells.length] = "SET-MINIMUM-SIZE";
options[layerCells.length+1] = "CLEAR-MINIMUM-SIZE";
String initial = options[0];
Cell cell = getLayerCell(ni);
if (cell != null) initial = cell.getName().substring(6);
String choice = PromptAt.showPromptAt(wnd, ni, "Change Layer", "New layer for this geometry:", initial, options);
if (choice == null) return;
// save the results
new ModifyLayerJob(ni, choice, layerCells);
}
/**
* Class to modify a port object in a node of the technology editor.
*/
private static class ModifyLayerJob extends Job
{
private NodeInst ni;
private String choice;
private Cell [] layerCells;
private ModifyLayerJob(NodeInst ni, String choice, Cell [] layerCells)
{
super("Change Layer Information", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.choice = choice;
this.layerCells = layerCells;
startJob();
}
public boolean doIt() throws JobException
{
if (choice.equals("SET-MINIMUM-SIZE"))
{
if (!ni.getParent().getName().startsWith("node-"))
{
System.out.println("Can only set minimum size in node descriptions");
return true;
}
ni.newDisplayVar(Info.MINSIZEBOX_KEY, "MIN");
return true;
}
if (choice.equals("CLEAR-MINIMUM-SIZE"))
{
if (ni.getVar(Info.MINSIZEBOX_KEY) == null)
{
System.out.println("Minimum size is not set on this layer");
return true;
}
ni.delVar(Info.MINSIZEBOX_KEY);
return true;
}
// find the actual cell with that layer specification
for(int i=0; i<layerCells.length; i++)
{
if (choice.equals(layerCells[i].getName().substring(6)))
{
// found the name, set the patch
LayerInfo li = LayerInfo.parseCell(layerCells[i]);
if (li != null)
{
setPatch(ni, li.desc);
ni.newVar(Info.LAYER_KEY, layerCells[i].getId());
}
return true;
}
}
System.out.println("Cannot find layer primitive " + choice);
return true;
}
}
/**
* Method to modify port characteristics
*/
private static void modPort(EditWindow wnd, NodeInst ni)
{
// count the number of arcs in this technology
List<Cell> allArcs = new ArrayList<Cell>();
for(Iterator<Cell> it = ni.getParent().getLibrary().getCells(); it.hasNext(); )
{
Cell cell = it.next();
if (cell.getName().startsWith("arc-")) allArcs.add(cell);
}
// make a set of those arcs which can connect to this port
Set<NodeProto> connectSet = new HashSet<NodeProto>();
Variable var = ni.getVar(Info.CONNECTION_KEY);
if (var != null)
{
CellId [] connects = (CellId [])var.getObject();
for(int i=0; i<connects.length; i++)
{
if (connects[i] != null)
connectSet.add(EDatabase.clientDatabase().getCell(connects[i]));
}
}
// build an array of arc connections
PromptAt.Field [] fields = new PromptAt.Field[allArcs.size()+3];
for(int i=0; i<allArcs.size(); i++)
{
Cell cell = allArcs.get(i);
boolean doesConnect = connectSet.contains(cell);
fields[i] = new PromptAt.Field(cell.getName().substring(4),
new String [] {"Allowed", "Disallowed"}, (doesConnect ? "Allowed" : "Disallowed"));
}
Variable angVar = ni.getVar(Info.PORTANGLE_KEY);
int ang = 0;
if (angVar != null) ang = ((Integer)angVar.getObject()).intValue();
Variable rangeVar = ni.getVar(Info.PORTRANGE_KEY);
int range = 180;
if (rangeVar != null) range = ((Integer)rangeVar.getObject()).intValue();
Variable meaningVar = ni.getVar(Info.PORTMEANING_KEY);
int meaning = 0;
if (meaningVar != null) meaning = ((Integer)meaningVar.getObject()).intValue();
fields[allArcs.size()] = new PromptAt.Field("Angle:", TextUtils.formatDouble(ang));
fields[allArcs.size()+1] = new PromptAt.Field("Angle range:", TextUtils.formatDouble(range));
String[] meanings = new String[]{"No meaning", "Gate", "Gated"};
fields[allArcs.size()+2] = new PromptAt.Field("Transistor meaning:", meanings, meanings[meaning]);
String choice = PromptAt.showPromptAt(wnd, ni, "Change Port", fields);
if (choice == null) return;
// save the results
String [] fieldValues = new String[fields.length];
for(int i=0; i<fields.length; i++)
fieldValues[i] = (String)fields[i].getFinal();
new ModifyPortJob(ni, allArcs, fieldValues);
}
/**
* Class to modify a port object in a node of the technology editor.
*/
private static class ModifyPortJob extends Job
{
private NodeInst ni;
private List<Cell> allArcs;
private String [] fieldValues;
private ModifyPortJob(NodeInst ni, List<Cell> allArcs, String [] fieldValues)
{
super("Change Port Information", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.allArcs = allArcs;
this.fieldValues = fieldValues;
startJob();
}
public boolean doIt() throws JobException
{
int numConnects = 0;
for(int i=0; i<allArcs.size(); i++)
{
String answer = fieldValues[i];
if (answer.equals("Allowed")) numConnects++;
}
CellId [] newConnects = new CellId[numConnects];
int k = 0;
for(int i=0; i<allArcs.size(); i++)
{
String answer = fieldValues[i];
if (answer.equals("Allowed")) newConnects[k++] = allArcs.get(i).getId();
}
ni.newVar(Info.CONNECTION_KEY, newConnects);
int newAngle = TextUtils.atoi(fieldValues[allArcs.size()]);
ni.newVar(Info.PORTANGLE_KEY, new Integer(newAngle));
int newRange = TextUtils.atoi(fieldValues[allArcs.size()+1]);
ni.newVar(Info.PORTRANGE_KEY, new Integer(newRange));
String newMeaning = fieldValues[allArcs.size()+2];
int meaning = 0;
if (newMeaning.equals("Gate")) meaning = 1; else
if (newMeaning.equals("Gated")) meaning = 2;
ni.newVar(Info.PORTMEANING_KEY, new Integer(meaning));
return true;
}
}
private static void modArcFunction(EditWindow wnd, NodeInst ni)
{
String initialFuncName = Info.getValueOnNode(ni);
List<ArcProto.Function> funs = ArcProto.Function.getFunctions();
String [] functionNames = new String[funs.size()];
for(int i=0; i<funs.size(); i++)
{
ArcProto.Function fun = funs.get(i);
functionNames[i] = fun.toString();
}
String choice = PromptAt.showPromptAt(wnd, ni, "Change Arc Function", "New function for this arc:", initialFuncName, functionNames);
if (choice == null) return;
new SetTextJob(ni, "Function: " + choice);
}
private static void modArcFixAng(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set whether this Arc Remains at a Fixed Angle",
"Should instances of this arc be created with the 'fixed angle' constraint?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Fixed-angle: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modArcWipes(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set Whether this Arc Can Obscure a Pin Node",
"Can this arc obscure a pin node (that is obscurable)?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Wipes pins: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modArcExtension(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set Extension Default",
"Are new instances of this arc drawn with ends extended?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Extend arcs: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modArcAngInc(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newInc = PromptAt.showPromptAt(wnd, ni, "Change Angle Increment",
"New angular granularity for placing this type of arc:", initialMsg);
if (newInc != null) new SetTextJob(ni, "Angle increment: " + newInc);
}
private static void modNodeFunction(EditWindow wnd, NodeInst ni)
{
String initialFuncName = Info.getValueOnNode(ni);
List<PrimitiveNode.Function> funs = PrimitiveNode.Function.getFunctions();
String [] functionNames = new String[funs.size()];
for(int i=0; i<funs.size(); i++)
{
PrimitiveNode.Function fun = funs.get(i);
functionNames[i] = fun.toString();
}
String choice = PromptAt.showPromptAt(wnd, ni, "Change Node Function", "New function for this node:", initialFuncName, functionNames);
if (choice == null) return;
new SetTextJob(ni, "Function: " + choice);
}
private static void modNodeSerpentine(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set Serpentine Transistor Capability",
"Is this node a serpentine transistor?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Serpentine transistor: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modNodeSquare(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Does Node Remain Square",
"Must this node remain square?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Square node: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modNodeWipes(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set How Arcs Obscure This Node",
"Is this node invisible when 1 or 2 arcs connect to it?", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Invisible with 1 or 2 arcs: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modNodeSpiceTemplate(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newST = PromptAt.showPromptAt(wnd, ni, "Change Spice Template",
"New Spice Template for this node:", initialMsg);
if (newST != null) new SetTextJob(ni, "Spice Template: " + newST);
}
// private static void modNodeCustomOverrides(EditWindow wnd, NodeInst ni)
// {
// Variable var = ni.getVar(Artwork.ART_MESSAGE);
// if (var == null) return;
// String [] lines = (String[])var.getObject();
//
// PromptAt.Field [] fields = new PromptAt.Field[lines.length+1];
// int j = 0;
// for(int i=1; i<lines.length; i++)
// {
// fields[j] = new PromptAt.Field("Override " + (j+1) + ":", lines[i]);
// j++;
// }
// for(int i=0; i<2; i++)
// {
// fields[j] = new PromptAt.Field("New Override " + (j+1) + ":", "");
// j++;
// }
// String choice = PromptAt.showPromptAt(wnd, ni, "Adjust Custom Overrides", fields);
// if (choice == null) return;
//
// j = 0;
// for(int i=0; i<=lines.length; i++)
// if (((String)fields[i].getFinal()).trim().length() > 0) j++;
// String [] newLines = new String[j+1];
// newLines[0] = lines[0];
// j = 1;
// for(int i=0; i<=lines.length; i++)
// {
// String str = (String)fields[i].getFinal();
// if (str.trim().length() > 0) newLines[j++] = str.trim();
// }
// new SetTextJob(ni, newLines);
// }
private static void modNodeLockability(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
boolean initialChoice = initialMsg.equalsIgnoreCase("yes");
boolean finalChoice = PromptAt.showPromptAt(wnd, ni, "Set Node Lockability",
"Is this node able to be locked down (used for FPGA primitives):", initialChoice);
if (finalChoice != initialChoice)
{
new SetTextJob(ni, "Lockable: " + (finalChoice ? "Yes" : "No"));
}
}
private static void modTechShortName(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Set Short Name",
"The Short Name of this technology:", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Short Name: " + newUnit);
}
private static void modTechScale(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Set Unit Size",
"The scale of this technology (nanometers per grid unit):", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Lambda: " + newUnit);
}
private static void modTechFoundry(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newUnit = PromptAt.showPromptAt(wnd, ni, "Set Default Foundry",
"The default foundry for this technology:", initialMsg);
if (newUnit != null) new SetTextJob(ni, "Foundry: " + newUnit);
}
private static void modTechNumMetals(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newDesc = PromptAt.showPromptAt(wnd, ni, "Set Number of Metal Layers",
"Number of Metal Layers in this technology:", initialMsg);
if (newDesc != null) new SetTextJob(ni, "Number of Metal Layers: " + newDesc);
}
private static void modTechDescription(EditWindow wnd, NodeInst ni)
{
String initialMsg = Info.getValueOnNode(ni);
String newDesc = PromptAt.showPromptAt(wnd, ni, "Set Technology Description",
"Full description of this technology:", initialMsg);
if (newDesc != null) new SetTextJob(ni, "Description: " + newDesc);
}
/****************************** UTILITIES ******************************/
/**
* Class to create a technology-library from a technology.
*/
private static class SetTextJob extends Job
{
private NodeInst ni;
private Object chr;
private SetTextJob(NodeInst ni, Object chr)
{
super("Make Technology Library from Technology", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.chr = chr;
startJob();
}
public boolean doIt() throws JobException
{
ni.newDisplayVar(Artwork.ART_MESSAGE, chr);
return true;
}
}
/**
* Class to set transparent colors on a technology.
*/
private static class SetTransparentColorJob extends Job
{
private NodeInst ni;
private String chr;
private SetTransparentColorJob(NodeInst ni, String chr)
{
super("Set Transparent Colors", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.ni = ni;
this.chr = chr;
startJob();
}
public boolean doIt() throws JobException
{
ni.newVar(Info.TRANSLAYER_KEY, chr);
return true;
}
}
/**
* Class to create a technology-library from a technology.
*/
private static class RedoLayerGraphicsJob extends Job
{
private Cell cell;
private RedoLayerGraphicsJob(Cell cell)
{
super("Redo Layer Graphics", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
startJob();
}
public boolean doIt() throws JobException
{
NodeInst patchNi = null;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getProto() != Artwork.tech().filledBoxNode) continue;
int opt = getOptionOnNode(ni);
if (opt == Info.LAYERPATTERN) continue;
patchNi = ni;
break;
}
if (patchNi == null) return false;
// get the current description of this layer
LayerInfo li = LayerInfo.parseCell(cell);
if (li == null) return false;
// modify the demo patch to reflect the color and pattern
setPatch(patchNi, li.desc);
// now do this to all layers in all cells!
for(Iterator<Cell> cIt = cell.getLibrary().getCells(); cIt.hasNext(); )
{
Cell onp = cIt.next();
if (!onp.getName().startsWith("arc-") && !onp.getName().startsWith("node-")) continue;
for(Iterator<NodeInst> nIt = onp.getNodes(); nIt.hasNext(); )
{
NodeInst cNi = nIt.next();
if (getOptionOnNode(cNi) != Info.LAYERPATCH) continue;
Cell varCell = getLayerCell(cNi);
// Variable varLay = cNi.getVar(Info.LAYER_KEY);
// if (varLay == null) continue;
// CellId cID = (CellId)varLay.getObject();
// Cell varCell = EDatabase.serverDatabase().getCell(cID);
if (varCell != cell) continue;
setPatch(cNi, li.desc);
}
}
return true;
}
}
static void setPatch(NodeInst ni, EGraphics desc)
{
if (desc.getTransparentLayer() > 0)
{
ni.newVar(Artwork.ART_COLOR, new Integer(EGraphics.makeIndex(desc.getTransparentLayer())));
} else
{
ni.newVar(Artwork.ART_COLOR, new Integer(EGraphics.makeIndex(desc.getColor())));
}
if (desc.isPatternedOnDisplay())
{
int [] raster = desc.getPattern();
Integer [] pattern = new Integer[17];
for(int i=0; i<16; i++) pattern[i] = new Integer(raster[i]);
pattern[16] = new Integer(desc.getOutlined().getIndex());
ni.newVar(Artwork.ART_PATTERN, pattern);
} else
{
if (ni.getVar(Artwork.ART_PATTERN) != null)
ni.delVar(Artwork.ART_PATTERN);
}
}
/**
* Method to set the layer-pattern squares of cell "np" to the bits in "desc".
*/
private static void setLayerPattern(Cell np, int [] pattern)
{
// look at all nodes in the layer description cell
int patternCount = 0;
Rectangle2D patternBounds = null;
for(Iterator<NodeInst> it = np.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getProto() == Artwork.tech().boxNode || ni.getProto() == Artwork.tech().filledBoxNode)
{
Variable var = ni.getVar(Info.OPTION_KEY);
if (var == null) continue;
if (((Integer)var.getObject()).intValue() != Info.LAYERPATTERN) continue;
Rectangle2D bounds = ni.getBounds();
if (patternCount == 0)
{
patternBounds = bounds;
} else
{
Rectangle2D.union(patternBounds, bounds, patternBounds);
}
patternCount++;
}
}
if (patternCount != 16*16 && patternCount != 16*8)
{
System.out.println("Incorrect number of pattern boxes in " + np +
" (has " + patternCount + ", not " + (16*16) + ")");
return;
}
// set the pattern
for(Iterator<NodeInst> it = np.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getProto() != Artwork.tech().boxNode && ni.getProto() != Artwork.tech().filledBoxNode) continue;
Variable var = ni.getVar(Info.OPTION_KEY);
if (var == null) continue;
if (((Integer)var.getObject()).intValue() != Info.LAYERPATTERN) continue;
Rectangle2D niBounds = ni.getBounds();
int x = (int)((niBounds.getMinX() - patternBounds.getMinX()) / (patternBounds.getWidth() / 16));
int y = (int)((patternBounds.getMaxY() - niBounds.getMaxY()) / (patternBounds.getHeight() / 16));
int wantColor = 0;
if ((pattern[y] & (1 << (15-x))) != 0) wantColor = 0xFFFF;
int color = getLayerColor(ni);
if (color != wantColor)
new SetLayerPatternJob(ni, wantColor);
}
}
/**
* Method to return the option index of node "ni"
*/
public static int getOptionOnNode(NodeInst ni)
{
// port objects are readily identifiable
if (ni.getProto() == Generic.tech().portNode) return Info.PORTOBJ;
// center objects are also readily identifiable
if (ni.getProto() == Generic.tech().cellCenterNode) return Info.CENTEROBJ;
Variable var = ni.getVar(Info.OPTION_KEY);
if (var == null) return -1;
int option = ((Integer)var.getObject()).intValue();
if (option == Info.LAYERPATCH)
{
// may be a highlight object
Variable var2 = ni.getVar(Info.LAYER_KEY);
if (var2 != null)
{
if (var2.getObject() == null) return Info.HIGHLIGHTOBJ;
}
}
return option;
}
/******************** SUPPORT ROUTINES ********************/
public static void reorderPrimitives(int type)
{
new RearrangeOrder(type);
}
/**
* This class displays a dialog for rearranging layers, arcs, or nodes in a technology library.
*/
private static class RearrangeOrder extends EDialog
{
private JList list;
private DefaultListModel model;
private Library lib;
private int type;
private int startItem;
private int endItem;
private boolean endBefore;
private int endLineHighlightBefore;
/** Creates new form Rearrange technology components */
private RearrangeOrder(int type)
{
super(null, true);
this.type = type;
lib = Library.getCurrent();
switch (type)
{
case 1: setTitle("Rearrange Layer Order"); break;
case 2: setTitle("Rearrange Arc Order"); break;
case 3: setTitle("Rearrange Node Order"); break;
}
setName("");
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent evt) { exit(false); }
});
getContentPane().setLayout(new GridBagLayout());
JLabel title = new JLabel("Drag to reorganize the list");
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(title, gbc);
JScrollPane center = new JScrollPane();
center.setPreferredSize(new Dimension(300, 150));
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.weightx = 1; gbc.weighty = 1;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(center, gbc);
model = new DefaultListModel();
list = new JList(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
center.setViewportView(list);
DragListener dl = new DragListener();
list.addMouseListener(dl);
list.addMouseMotionListener(dl);
list.setCellRenderer(new MyCellRenderer());
model.clear();
String [] listNames = null;
switch (type)
{
case 1: listNames = getLayerNameList(); break;
case 2: listNames = getArcNameList(); break;
case 3: listNames = getNodeNameList(); break;
}
for(int i=0; i<listNames.length; i++)
model.addElement(listNames[i]);
list.setSelectedIndex(-1);
// OK and Cancel
JButton cancel = new JButton("Cancel");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 4;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(cancel, gbc);
cancel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(false); }
});
JButton ok = new JButton("OK");
getRootPane().setDefaultButton(ok);
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 4;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(ok, gbc);
ok.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { exit(true); }
});
pack();
setVisible(true);
}
protected void escapePressed() { exit(false); }
/**
* Method to handle the OK button
*/
private void exit(boolean goodButton)
{
if (goodButton)
{
String [] newList = new String[model.size()];
for(int i=0; i<model.size(); i++)
newList[i] = (String)model.getElementAt(i);
new UpdateOrderingJob(lib, newList, type);
}
dispose();
}
/**
* Class to handle saving the new orderings in a Job.
*/
private static class UpdateOrderingJob extends Job
{
private Library lib;
private String [] newList;
private int type;
private UpdateOrderingJob(Library lib, String [] newList, int type)
{
super("Update Ordering", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.lib = lib;
this.newList = newList;
this.type = type;
startJob();
}
public boolean doIt() throws JobException
{
switch (type)
{
case 1: lib.newVar(Info.LAYERSEQUENCE_KEY, newList); break;
case 2: lib.newVar(Info.ARCSEQUENCE_KEY, newList); break;
case 3: lib.newVar(Info.NODESEQUENCE_KEY, newList); break;
}
return true;
}
public void terminateOK()
{
// force redraw of explorer tree
WindowFrame.wantToRedoLibraryTree();
}
}
/**
* Class to handle selection display in the list.
* Disables normal selection and instead shows only what is being dragged.
*/
private class MyCellRenderer extends JLabel implements ListCellRenderer
{
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)
{
String s = value.toString();
setText(s);
if (index == startItem)
{
setBackground(list.getForeground());
setForeground(list.getBackground());
} else
{
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
return this;
}
}
/**
* Class to handle clicks and drags to rearrange the list.
*/
private class DragListener implements MouseListener, MouseMotionListener
{
public void mousePressed(MouseEvent e)
{
startItem = list.locationToIndex(e.getPoint());
endItem = startItem;
endBefore = true;
endLineHighlightBefore = -1;
}
public void mouseDragged(MouseEvent e)
{
highlightEndItem(e);
}
public void mouseReleased(MouseEvent e)
{
removeHighlight();
// rearrange the list
int newIndex = endItem;
if (!endBefore) newIndex++;
if (newIndex > startItem) newIndex--;
Object was = model.getElementAt(startItem);
model.remove(startItem);
model.add(newIndex, was);
startItem = -1;
}
public void mouseMoved(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
private void highlightEndItem(MouseEvent e)
{
endItem = list.locationToIndex(e.getPoint());
int height = list.getFixedCellHeight();
Point pt = list.indexToLocation(endItem);
endBefore = e.getPoint().y < pt.y - height/2;
removeHighlight();
if (startItem == endItem) return;
if (startItem == endItem-1 && endBefore) return;
if (startItem == endItem+1 && !endBefore) return;
endLineHighlightBefore = endItem;
if (!endBefore) endLineHighlightBefore++;
Graphics g = list.getGraphics();
pt = list.indexToLocation(endLineHighlightBefore);
Rectangle rect = list.getBounds();
g.setColor(Color.RED);
g.drawLine(rect.x, pt.y, rect.x+rect.width, pt.y);
}
private void removeHighlight()
{
if (endLineHighlightBefore >= 0)
{
Graphics g = list.getGraphics();
Point pt = list.indexToLocation(endLineHighlightBefore);
Rectangle rect = list.getBounds();
g.setColor(list.getBackground());
g.drawLine(rect.x, pt.y, rect.x+rect.width, pt.y);
endLineHighlightBefore = -1;
}
}
}
}
static class DocColumn
{
List<String> elements;
String header;
int maxWid = 0;
DocColumn(String header, int numE)
{
this.header = header;
this.maxWid = header.length();
elements = new ArrayList<String>(numE);
}
void add(String el)
{
elements.add(el);
int len = el.length();
if (maxWid < len) maxWid = len;
}
private String getColumn(String s)
{
StringBuffer val = new StringBuffer(s);
val.ensureCapacity(maxWid);
int fillS = val.length();
for (int i = fillS; i < maxWid+2; i++) // add 2 extra spaces for better formatting
val.append(" "); // add the remaind spaces
return val.toString();
}
String getUnderlying()
{
StringBuffer s = new StringBuffer();
s.ensureCapacity(maxWid);
for (int i = 0; i < maxWid; i++)
s.append("-");
s.append(" "); // two extra spaces
return s.toString();
}
String getHeader()
{
return getColumn(header);
}
String get(int pos)
{
if (pos >= elements.size())
return "";
return getColumn(elements.get(pos));
}
static void printColumns(DocColumn[] cols, String title)
{
// print headers
StringBuffer header = new StringBuffer();
StringBuffer under = new StringBuffer();
int totalNumEls = 0;
for (DocColumn col : cols)
{
header.append(col.getHeader());
under.append(col.getUnderlying());
int numEls = col.elements.size();
if (numEls > totalNumEls) totalNumEls = numEls;
}
int numLine = header.length();
int stars = (numLine - title.length() - 4) / 2;
for(int i=0; i<stars; i++) System.out.print("*");
System.out.print(" " + title + " ");
for(int i=0; i<stars; i++) System.out.print("*");
System.out.println();
System.out.println(header.toString());
System.out.println(under.toString());
for (int i = 0; i < totalNumEls; i++)
{
for (DocColumn col : cols)
System.out.print(col.get(i));
System.out.println();
}
System.out.println();
}
}
/**
* Method to print detailled information about a given technology.
* @param tech the technology to describe.
*/
public static void describeTechnology(Technology tech)
{
// ***************************** dump layers ******************************
// allocate space for all layer fields
int layerCount = tech.getNumLayers();
Map<Layer,String> gdsLayers = tech.getGDSLayers();
DocColumn[] cols = new DocColumn[7];
cols[0] = new DocColumn("Layer", layerCount);
cols[1] = new DocColumn("Color", layerCount);
cols[2] = new DocColumn("Style", layerCount);
cols[3] = new DocColumn("CIF", layerCount);
cols[4] = new DocColumn("GDS", layerCount);
cols[5] = new DocColumn("Function", layerCount);
cols[6] = new DocColumn("Coverage", layerCount);
// Adding the layers
for (Iterator<Layer> it = tech.getLayers(); it.hasNext();)
{
Layer layer = it.next();
// Name
cols[0].add(layer.getName());
// Transparency
EGraphics gra = layer.getGraphics();
if (gra.getTransparentLayer() > 0)
cols[1].add("Transparent " + gra.getTransparentLayer());
else
{
Color col = gra.getColor();
cols[1].add("(" + col.getRed() + "," + col.getGreen() + "," + col.getBlue() + ")");
}
// Style
String tmp = "?";
if (gra.isPatternedOnDisplay())
{
if (gra.getOutlined() != EGraphics.Outline.NOPAT)
tmp = "pat/outl";
else
tmp = "pat";
} else
{
tmp = "solid";
}
cols[2].add(tmp);
// CIF
cols[3].add(layer.getCIFLayer());
// GDS
String gdsLayer = gdsLayers.get(layer);
cols[4].add(gdsLayer != null ? gdsLayer : "");
// Function
cols[5].add(layer.getFunction().toString());
// Coverage
cols[6].add(TextUtils.formatDouble(LayerCoverageTool.LayerCoveragePreferences.DEFAULT_AREA_COVERAGE/*layer.getAreaCoverage()*/));
}
// write the layer information */
DocColumn.printColumns(cols, "LAYERS IN " + tech.getTechName().toUpperCase());
// ****************************** dump arcs ******************************
cols = new DocColumn[8];
int numArcs = tech.getNumArcs();
cols[0] = new DocColumn("Arc", numArcs);
cols[1] = new DocColumn("Layer", numArcs);
cols[2] = new DocColumn("Size", numArcs);
cols[3] = new DocColumn("Extend", numArcs);
cols[4] = new DocColumn("Angle", numArcs);
cols[5] = new DocColumn("Wipes", numArcs);
cols[6] = new DocColumn("Function", numArcs);
cols[7] = new DocColumn("Antenna", numArcs);
for(Iterator<ArcProto> it = tech.getArcs(); it.hasNext(); )
{
ArcProto ap = it.next();
Poly [] polys = ap.getShapeOfDummyArc(4000);
for(int k=0; k<polys.length; k++)
{
String name = "", extend = "", increment = "", wipe = "", func = "", antenna = "";
if (k == 0) // first time only
{
// Arc name
name = ap.getName();
// Extended
extend = ap.getFactoryDefaultInst().isTailExtended() ? "yes" : "no";
// Increment
increment = String.valueOf(ap.getFactoryAngleIncrement());
// Wipable
wipe = ap.isWipable() ? "yes" : "no";
// Function
func = ap.getFunction().toString();
// Antenna
antenna = TextUtils.formatDouble(ap.getFactoryAntennaRatio());
}
// Arc name
cols[0].add(name);
// Extended
cols[3].add(extend);
// Increment
cols[4].add(increment);
// Wipable
cols[5].add(wipe);
// Function
cols[6].add(func);
// Antenna
cols[7].add(antenna);
Poly poly = polys[k];
// Layer name
cols[1].add(poly.getLayer().getName());
Rectangle2D bounds = poly.getBounds2D();
double width = Math.min(bounds.getWidth(), bounds.getHeight());
cols[2].add(TextUtils.formatDouble(width));
}
}
// write the arc information */
DocColumn.printColumns(cols, "ARCS IN " + tech.getTechName().toUpperCase());
// ****************************** dump nodes ******************************
cols = new DocColumn[8];
int numNodes = tech.getNumNodes();
cols[0] = new DocColumn("Node", numNodes);
cols[1] = new DocColumn("Function", numNodes);
cols[2] = new DocColumn("Layers", numNodes);
cols[3] = new DocColumn("Size (#instances)", numNodes);
cols[4] = new DocColumn("Ports", numNodes);
cols[5] = new DocColumn("Size", numNodes);
cols[6] = new DocColumn("Angle", numNodes);
cols[7] = new DocColumn("Connections", numNodes);
for(Iterator<PrimitiveNode> it = tech.getNodes(); it.hasNext(); )
{
PrimitiveNode np = it.next();
if (np.isNotUsed()) continue; // not need of reporting it. Valid when foundry is not Mosis in 180nm,
NodeInst ni = NodeInst.makeDummyInstance(np);
Poly [] polys = tech.getShapeOfNode(ni);
Map<Layer, List<Poly>> map = new HashMap<Layer,List<Poly>>();
for (Poly p : polys)
{
List<Poly> list = map.get(p.getLayer());
if (list == null)
{
list = new ArrayList<Poly>(1);
map.put(p.getLayer(), list);
}
list.add(p);
}
boolean firstTime = true;
int numLayers = 0;
for (Layer layer : map.keySet())
{
Set<Rectangle2D> set = new HashSet<Rectangle2D>();
List<Poly> list = map.get(layer);
for (Poly p : list)
{
Rectangle2D bound = p.getBounds2D();
ERectangle size = ERectangle.fromLambda(0, 0, bound.getWidth(), bound.getHeight());
set.add(size);
}
// only if all are identical
int size = list.size();
boolean allIdentical = set.size() == 1;
if (allIdentical)
{
Poly p = list.get(0);
list.clear();
list.add(p); // leaves only 1
}
for (Poly poly : list)
{
String name = "", function = "";
if (firstTime)
{
firstTime = false;
name = np.getName();
function = np.getFunction().getName();
}
// Name
cols[0].add(name);
// Function
cols[1].add(function);
// Layer name
cols[2].add(poly.getLayer().getName());
Rectangle2D polyBounds = poly.getBounds2D();
// Layer Size
String sizeLabel = TextUtils.formatDouble(polyBounds.getWidth()) + " x " +
TextUtils.formatDouble(polyBounds.getHeight());
if (allIdentical && size > 1)
sizeLabel += " ("+size+")";
cols[3].add(sizeLabel);
numLayers++;
}
}
int countPorts = 0, extra = 0;
for(Iterator<PortProto> pIt = np.getPorts(); pIt.hasNext(); )
{
PrimitivePort pp = (PrimitivePort)pIt.next();
// Port Name
cols[4].add(pp.getName());
Poly portPoly = ni.getShapeOfPort(pp);
Rectangle2D portRect = portPoly.getBounds2D();
// Port Size
cols[5].add(TextUtils.formatDouble(portRect.getWidth()) + " x " +
TextUtils.formatDouble(portRect.getHeight()));
// Port Angle
cols[6].add((pp.getAngleRange() == 180) ? "" : String.valueOf(pp.getAngle()));
int m = 0;
ArcProto [] conList = pp.getConnections();
for(ArcProto proto : conList)
{
if (proto.getTechnology() != tech) continue;
if (m > 0)
{
// adding empty strings to previous columns
if ((countPorts+extra+m) >= numLayers)
{
cols[0].add("");
cols[1].add("");
cols[2].add("");
cols[3].add("");
}
cols[4].add("");
cols[5].add("");
cols[6].add("");
extra++;
}
// Connection
cols[7].add(proto.getName());
m++;
}
if (m == 0)
cols[7].add("<NONE>");
countPorts++;
}
for (int i = (countPorts+extra); i < numLayers; i++)
{
cols[4].add("");
cols[5].add("");
cols[6].add("");
cols[7].add("");
}
}
// write the node information */
DocColumn.printColumns(cols, "NODES IN " + tech.getTechName().toUpperCase());
}
// /*
// * Routine for editing the DRC tables.
// */
// void us_teceditdrc(void)
// {
// REGISTER INTBIG i, changed, nodecount;
// NODEPROTO **nodesequence;
// LIBRARY *liblist[1];
// REGISTER VARIABLE *var;
// REGISTER DRCRULES *rules;
//
// // get the current list of layer and node names
// getLayerNameList();
// liblist[0] = el_curlib;
// nodecount = findCellSequence(liblist, "node-", NODESEQUENCE_KEY);
//
// // create a RULES structure
// rules = dr_allocaterules(us_teceddrclayers, nodecount, x_("EDITED TECHNOLOGY"));
// if (rules == NODRCRULES) return;
// for(i=0; i<us_teceddrclayers; i++)
// (void)allocstring(&rules.layernames[i], us_teceddrclayernames[i], el_tempcluster);
// for(i=0; i<nodecount; i++)
// (void)allocstring(&rules.nodenames[i], &nodesequence[i].protoname[5], el_tempcluster);
// if (nodecount > 0) efree((CHAR *)nodesequence);
//
// // get the text-list of design rules and convert them into arrays
// var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING|VISARRAY, x_("EDTEC_DRC"));
// us_teceditgetdrcarrays(var, rules);
//
// // edit the design-rule arrays
// changed = dr_rulesdlog(NOTECHNOLOGY, rules);
//
// // if changes were made, convert the arrays back into a text-list
// if (changed != 0)
// {
// us_tecedloaddrcmessage(rules, el_curlib);
// }
//
// // free the arrays
// dr_freerules(rules);
// }
//
// /*
// * Routine to create arrays describing the design rules in the variable "var" (which is
// * from "EDTEC_DRC" on a library). The arrays are stored in "rules".
// */
// void us_teceditgetdrcarrays(VARIABLE *var, DRCRULES *rules)
// {
// REGISTER INTBIG i, l;
// INTBIG amt;
// BOOLEAN connected, wide, multi, edge;
// INTBIG widrule, layer1, layer2, j;
// REGISTER CHAR *str, *pt;
// CHAR *rule;
//
// // get the design rules
// if (var == NOVARIABLE) return;
//
// l = getlength(var);
// for(i=0; i<l; i++)
// {
// // parse the DRC rule
// str = ((CHAR **)var.addr)[i];
// while (*str == ' ') str++;
// if (*str == 0) continue;
//
// // special case for node minimum size rule
// if (*str == 'n')
// {
// str++;
// for(pt = str; *pt != 0; pt++) if (*pt == ' ') break;
// if (*pt == 0)
// {
// ttyputmsg(_("Bad node size rule (line %ld): %s"), i+1, str);
// continue;
// }
// *pt = 0;
// for(j=0; j<rules.numnodes; j++)
// if (namesame(str, rules.nodenames[j]) == 0) break;
// *pt = ' ';
// if (j >= rules.numnodes)
// {
// ttyputmsg(_("Unknown node (line %ld): %s"), i+1, str);
// continue;
// }
// while (*pt == ' ') pt++;
// rules.minnodesize[j*2] = atofr(pt);
// while (*pt != 0 && *pt != ' ') pt++;
// while (*pt == ' ') pt++;
// rules.minnodesize[j*2+1] = atofr(pt);
// while (*pt != 0 && *pt != ' ') pt++;
// while (*pt == ' ') pt++;
// if (*pt != 0) reallocstring(&rules.minnodesizeR[j], pt, el_tempcluster);
// continue;
// }
//
// // parse the layer rule
// if (us_tecedgetdrc(str, &connected, &wide, &multi, &widrule, &edge,
// &amt, &layer1, &layer2, &rule, rules.numlayers, rules.layernames))
// {
// ttyputmsg(_("DRC line %ld is: %s"), i+1, str);
// continue;
// }
//
// // set the layer spacing
// if (widrule == 1)
// {
// rules.minwidth[layer1] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.minwidthR[layer1], rule, el_tempcluster);
// } else if (widrule == 2)
// {
// rules.widelimit = amt;
// } else
// {
// if (layer1 > layer2) { j = layer1; layer1 = layer2; layer2 = j; }
// j = (layer1+1) * (layer1/2) + (layer1&1) * ((layer1+1)/2);
// j = layer2 + rules.numlayers * layer1 - j;
// if (edge)
// {
// rules.edgelist[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.edgelistR[j], rule, el_tempcluster);
// } else if (wide)
// {
// if (connected)
// {
// rules.conlistW[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.conlistWR[j], rule, el_tempcluster);
// } else
// {
// rules.unconlistW[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.unconlistWR[j], rule, el_tempcluster);
// }
// } else if (multi)
// {
// if (connected)
// {
// rules.conlistM[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.conlistMR[j], rule, el_tempcluster);
// } else
// {
// rules.unconlistM[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.unconlistMR[j], rule, el_tempcluster);
// }
// } else
// {
// if (connected)
// {
// rules.conlist[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.conlistR[j], rule, el_tempcluster);
// } else
// {
// rules.unconlist[j] = amt;
// if (*rule != 0)
// (void)reallocstring(&rules.unconlistR[j], rule, el_tempcluster);
// }
// }
// }
// }
// }
//
// /*
// * routine to parse DRC line "str" and fill the factors "connected" (set nonzero
// * if rule is for connected layers), "amt" (rule distance), "layer1" and "layer2"
// * (the layers). Presumes that there are "maxlayers" layer names in the
// * array "layernames". Returns true on error.
// */
// BOOLEAN us_tecedgetdrc(CHAR *str, BOOLEAN *connected, BOOLEAN *wide, BOOLEAN *multi, INTBIG *widrule,
// BOOLEAN *edge, INTBIG *amt, INTBIG *layer1, INTBIG *layer2, CHAR **rule, INTBIG maxlayers,
// CHAR **layernames)
// {
// REGISTER CHAR *pt;
// REGISTER INTBIG save;
//
// *connected = *wide = *multi = *edge = FALSE;
// for( ; *str != 0; str++)
// {
// if (tolower(*str) == 'c')
// {
// *connected = TRUE;
// continue;
// }
// if (tolower(*str) == 'w')
// {
// *wide = TRUE;
// continue;
// }
// if (tolower(*str) == 'm')
// {
// *multi = TRUE;
// continue;
// }
// if (tolower(*str) == 'e')
// {
// *edge = TRUE;
// continue;
// }
// break;
// }
// *widrule = 0;
// if (tolower(*str) == 's')
// {
// *widrule = 1;
// str++;
// } else if (tolower(*str) == 'l')
// {
// *widrule = 2;
// str++;
// }
//
// // get the distance
// pt = str;
// while (*pt != 0 && *pt != ' ' && *pt != '\t') pt++;
// while (*pt == ' ' || *pt == '\t') pt++;
// *amt = atofr(str);
//
// // get the first layer
// if (*widrule != 2)
// {
// str = pt;
// if (*str == 0)
// {
// ttyputerr(_("Cannot find layer names on DRC line"));
// return(TRUE);
// }
// while (*pt != 0 && *pt != ' ' && *pt != '\t') pt++;
// if (*pt == 0)
// {
// ttyputerr(_("Cannot find layer name on DRC line"));
// return(TRUE);
// }
// save = *pt;
// *pt = 0;
// for(*layer1 = 0; *layer1 < maxlayers; (*layer1)++)
// if (namesame(str, layernames[*layer1]) == 0) break;
// *pt++ = (CHAR)save;
// if (*layer1 >= maxlayers)
// {
// ttyputerr(_("First DRC layer name unknown"));
// return(TRUE);
// }
// while (*pt == ' ' || *pt == '\t') pt++;
// }
//
// // get the second layer
// if (*widrule == 0)
// {
// str = pt;
// while (*pt != 0 && *pt != ' ' && *pt != '\t') pt++;
// save = *pt;
// *pt = 0;
// for(*layer2 = 0; *layer2 < maxlayers; (*layer2)++)
// if (namesame(str, layernames[*layer2]) == 0) break;
// *pt = (CHAR)save;
// if (*layer2 >= maxlayers)
// {
// ttyputerr(_("Second DRC layer name unknown"));
// return(TRUE);
// }
// }
//
// while (*pt == ' ' || *pt == '\t') pt++;
// *rule = pt;
// return(FALSE);
// }
// /**
// * Method to examine the arrays describing the design rules and create
// * the variable "EDTEC_DRC" on library "lib".
// */
// void us_tecedloaddrcmessage(DRCRules rules, Library lib)
// {
// // load the arrays
// List drclist = new ArrayList();
//
// // write the minimum width for each layer
// for(i=0; i<rules.numlayers; i++)
// {
// DRCTemplate lr = drRules.getMinValue(layer, DRCTemplate.MINWID, foundry.techMode);
// if (lr == null) continue;
// String ruleMsg = "s" + lr.value1 + " " + layer.getName() + " " + lr.ruleName;
// drclist.add(ruleMsg);
// }
//
// // write the minimum size for each node
// for(i=0; i<rules.numnodes; i++)
// {
// if (rules.minnodesize[i*2] <= 0 && rules.minnodesize[i*2+1] <= 0) continue;
// {
// String ruleMsg = "n" + rules.nodenames[i] + " " + rules.minnodesize[i*2] + " " +
// rules.minnodesize[i*2+1] + " " + rules.minnodesizeR[i];
// drclist.add(ruleMsg);
// }
// }
//
// // now do the distance rules
// k = 0;
// for(i=0; i<rules.numlayers; i++) for(j=i; j<rules.numlayers; j++)
// {
// if (rules.conlist[k] >= 0)
// {
// infstr = initinfstr();
// formatinfstr(infstr, x_("c%s %s %s %s"), frtoa(rules.conlist[k]),
// rules.layernames[i], rules.layernames[j],
// rules.conlistR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.unconlist[k] >= 0)
// {
// infstr = initinfstr();
// formatinfstr(infstr, x_("%s %s %s %s"), frtoa(rules.unconlist[k]),
// rules.layernames[i], rules.layernames[j],
// rules.unconlistR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.conlistW[k] >= 0)
// {
// formatinfstr(infstr, x_("cw%s %s %s %s"), frtoa(rules.conlistW[k]),
// rules.layernames[i], rules.layernames[j],
// rules.conlistWR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.unconlistW[k] >= 0)
// {
// formatinfstr(infstr, x_("w%s %s %s %s"), frtoa(rules.unconlistW[k]),
// rules.layernames[i], rules.layernames[j],
// rules.unconlistWR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.conlistM[k] >= 0)
// {
// formatinfstr(infstr, x_("cm%s %s %s %s"), frtoa(rules.conlistM[k]),
// rules.layernames[i], rules.layernames[j],
// rules.conlistMR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.unconlistM[k] >= 0)
// {
// formatinfstr(infstr, x_("m%s %s %s %s"), frtoa(rules.unconlistM[k]),
// rules.layernames[i], rules.layernames[j],
// rules.unconlistMR[k]);
// drclist.add(ruleMsg);
// }
// if (rules.edgelist[k] >= 0)
// {
// formatinfstr(infstr, x_("e%s %s %s %s"), frtoa(rules.edgelist[k]),
// rules.layernames[i], rules.layernames[j],
// rules.edgelistR[k]);
// drclist.add(ruleMsg);
// }
// k++;
// }
//
// if (drclist.size() == 0)
// {
// // no rules: remove the variable
// if (lib.getVal("EDTEC_DRC") != null)
// lib.delVal("EDTEC_DRC");
// } else
// {
// lib.newVal("EDTEC_DRC", drclist);
// }
// }
}