/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: SpecialProperties.java * 2572968, exp 6/5/5 * Copyright (c) 2005 Sun Microsystems and Static Free Software * * 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.dialogs; import com.sun.electric.database.network.Network; import com.sun.electric.database.prototype.PortCharacteristic; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.variable.CodeExpression; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.database.variable.Variable; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.technology.technologies.Schematics; import com.sun.electric.tool.Job; import com.sun.electric.tool.JobException; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.ui.EditWindow; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JTextField; /** * Class to handle special dialogs for specific nodes. */ public class SpecialProperties { /** * Method to handle special dialogs that are associated with double-clicking on a node. * @param wnd the EditWindow in which the click occurred. * @param ni the NodeInst that was double-clicked. * @return 1 if the double-click has been handled. * 0 if text edit in-place should be done. * -1 indicates that a general "node properties" dialog should be shown. */ public static int doubleClickOnNode(EditWindow wnd, NodeInst ni) { // if double-clicked on a schematic resistor, show special dialog if (ni.getProto() == Schematics.tech().resistorNode) { NodePropertiesDialog npd = new NodePropertiesDialog(wnd, ni, "Resistance", Schematics.SCHEM_RESISTANCE, TextDescriptor.Unit.RESISTANCE); if (npd.wantMore()) return -1; return 1; } // if double-clicked on a schematic capacitor, show special dialog if (ni.getProto() == Schematics.tech().capacitorNode) { NodePropertiesDialog npd = new NodePropertiesDialog(wnd, ni, "Capacitance", Schematics.SCHEM_CAPACITANCE, TextDescriptor.Unit.CAPACITANCE); if (npd.wantMore()) return -1; return 1; } // if double-clicked on a schematic inductor, show special dialog if (ni.getProto() == Schematics.tech().inductorNode) { NodePropertiesDialog npd = new NodePropertiesDialog(wnd, ni, "Inductance", Schematics.SCHEM_INDUCTANCE, TextDescriptor.Unit.INDUCTANCE); if (npd.wantMore()) return -1; return 1; } // if double-clicked on a schematic diode, show special dialog if (ni.getProto() == Schematics.tech().diodeNode) { NodePropertiesDialog npd = new NodePropertiesDialog(wnd, ni, "Diode area", Schematics.SCHEM_DIODE, null); if (npd.wantMore()) return -1; return 1; } // if double-clicked on a schematic transistor, show special dialog if (ni.getProto() == Schematics.tech().transistorNode || ni.getProto() == Schematics.tech().transistor4Node) { PrimitiveNode.Function fun = ni.getFunction(); if (fun == PrimitiveNode.Function.TRA4NPN || fun == PrimitiveNode.Function.TRA4PNP || fun == PrimitiveNode.Function.TRANPN || fun == PrimitiveNode.Function.TRAPNP) { // show just the area value for NPN and PNP transistors NodePropertiesDialog npd = new NodePropertiesDialog(wnd, ni, "Transistor area", Schematics.ATTR_AREA, null); if (npd.wantMore()) return -1; return 1; } // show length and width for other transistors TransistorPropertiesDialog tpd = new TransistorPropertiesDialog(wnd, ni); if (tpd.wantMore()) return -1; return 1; } // if double-clicked on a schematic global, show special dialog if (ni.getProto() == Schematics.tech().globalNode) { GlobalPropertiesDialog gpd = new GlobalPropertiesDialog(wnd, ni); if (gpd.wantMore()) return -1; return 1; } return 0; } private static TextUtils.UnitScale getUnit(String str) { // get list of scales, sorted by the length of the postfix string TextUtils.UnitScale [] scales = TextUtils.UnitScale.getUnitScales(); List<TextUtils.UnitScale> scalesByPostfixLength = new ArrayList<TextUtils.UnitScale>(); for(int i=0; i<scales.length; i++) scalesByPostfixLength.add(scales[i]); Collections.sort(scalesByPostfixLength, new UnitsByPostfixLength()); for (TextUtils.UnitScale u : scalesByPostfixLength) { String postfix = u.getPostFix(); if (postfix.equals("")) continue; // ignore the NONE suffix case if (postfix.length() >= str.length()) continue; // postfix is same length or longer than string String sSuffix = str.substring(str.length()-postfix.length(), str.length()); if (sSuffix.equalsIgnoreCase(postfix)) return u; } return TextUtils.UnitScale.NONE; } /** * Comparator class for sorting Units by the length of their postfix. */ private static class UnitsByPostfixLength implements Comparator<TextUtils.UnitScale> { /** * Method to sort Units by their postfix length. */ public int compare(TextUtils.UnitScale u1, TextUtils.UnitScale u2) { String s1 = u1.getPostFix(); String s2 = u2.getPostFix(); return s2.length()- s1.length(); } } /** * This class displays a dialog for handling special node properties. */ private static class NodePropertiesDialog extends EDialog { private JTextField value; private JComboBox combo; private boolean cancelHit; private boolean moreHit; /** Creates new special node properties object */ private NodePropertiesDialog(EditWindow wnd, NodeInst ni, String title, Variable.Key key, TextDescriptor.Unit units) { super(null, true); TextUtils.UnitScale [] scales = TextUtils.UnitScale.getUnitScales(); String [] theScales = new String[scales.length]; for(int i=0; i<scales.length; i++) theScales[i] = scales[i].toString(); double num = 0; TextUtils.UnitScale scale = TextUtils.UnitScale.NONE; Variable var = ni.getVar(key); if (var != null) { String val = var.getObject().toString(); scale = getUnit(val); String postFix = scale.getPostFix(); if (postFix.length() > 0) val = val.substring(0, val.length()-postFix.length()); num = TextUtils.atof(val); } initComponents(wnd, ni, title, TextUtils.formatDouble(num), theScales, scale.getName(), units); cancelHit = moreHit = false; setVisible(true); toFront(); // all done: see if value should be updated if (!cancelHit) { String newValue = value.getText(); if (units != null) { int newScale = combo.getSelectedIndex(); newValue += scales[newScale].getPostFix(); } new ModifyNodeProperties(ni, key, newValue); } } protected void escapePressed() { exit(false); } private boolean wantMore() { return moreHit; } private void exit(boolean goodButton) { cancelHit = !goodButton; setVisible(false); dispose(); } private void moreButton() { moreHit = true; exit(true); } private void initComponents(EditWindow wnd, NodeInst ni, String title, String initialValue, String [] theScales, String initialScale, TextDescriptor.Unit unit) { getContentPane().setLayout(new GridBagLayout()); setTitle(title); setName(""); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { exit(false); } }); // special information value = new JTextField(initialValue); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; if (unit == null) gbc.gridwidth = 4; else gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = .5; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(value, gbc); EDialog.makeTextFieldSelectAllOnTab(value); value.selectAll(); if (unit != null) { combo = new JComboBox(); int selected = 0; for(int k=0; k<theScales.length; k++) { String sca = theScales[k]; if (sca.equalsIgnoreCase(initialScale)) selected = k; if (sca.length() == 0) combo.addItem(unit.getName()); else combo.addItem(sca + "-" + unit.getName().toLowerCase()); } combo.setSelectedIndex(selected); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 0; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = .5; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(combo, gbc); } // OK, More, and Cancel JButton cancel = new JButton("Cancel"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(cancel, gbc); cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(false); } }); JButton more = new JButton("More..."); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(more, gbc); more.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { moreButton(); } }); JButton ok = new JButton("OK"); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(ok, gbc); ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(true); } }); getRootPane().setDefaultButton(ok); pack(); // now make the dialog appear over a node Point ew = wnd.getLocationOnScreen(); Point locInWnd = wnd.databaseToScreen(ni.getAnchorCenterX(), ni.getAnchorCenterY()); Point textfield = value.getLocation(); Dimension textSize = value.getSize(); setLocation(locInWnd.x+ew.x-(textfield.x+textSize.width/2), locInWnd.y+ew.y-(textfield.y+textSize.height/2+20)); } } /** * Class for saving changes in the special dialog. */ private static class ModifyNodeProperties extends Job { private NodeInst ni; private Variable.Key key; private String newValue, newValueLen; private CodeExpression.Code newCode, newCodeLen; private int newBits; private ModifyNodeProperties(NodeInst ni, Variable.Key key, String newValue) { super("Change Node Value", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER); this.ni = ni; this.key = key; this.newValue = newValue; this.newBits = -1; startJob(); } private ModifyNodeProperties(NodeInst ni, Variable.Key key, String newValue, int newBits) { super("Change Node Value", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER); this.ni = ni; this.key = key; this.newValue = newValue; this.newBits = newBits; startJob(); } private ModifyNodeProperties(NodeInst ni, String newWid, CodeExpression.Code newWidCode, String newLen, CodeExpression.Code newLenCode) { super("Change Node Value", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER); this.ni = ni; this.key = null; this.newValue = newWid; this.newCode = newWidCode; this.newValueLen = newLen; this.newCodeLen = newLenCode; startJob(); } public boolean doIt() throws JobException { if (key == null) { // update length/width on transistor Variable oldWid = ni.getVar(Schematics.ATTR_WIDTH); TextDescriptor wtd = oldWid != null ? oldWid.getTextDescriptor() : TextDescriptor.getNodeTextDescriptor(); Object newV = Variable.withCode(newValue, newCode); ni.newVar(Schematics.ATTR_WIDTH, newV, wtd); Variable oldLen = ni.getVar(Schematics.ATTR_LENGTH); TextDescriptor ltd = oldLen != null ? oldLen.getTextDescriptor() : TextDescriptor.getNodeTextDescriptor(); Object newVL = Variable.withCode(newValueLen, newCodeLen); ni.newVar(Schematics.ATTR_LENGTH, newVL, ltd); } else { // update single value on a node Variable oldVar = ni.getVar(key); TextDescriptor td = oldVar != null ? oldVar.getTextDescriptor() : TextDescriptor.getNodeTextDescriptor(); CodeExpression.Code newCode = oldVar != null ? oldVar.getCode() : CodeExpression.Code.NONE; Object newV = Variable.withCode(newValue, newCode); ni.newVar(key, newV, td); // set techbits if requested if (newBits != -1) ni.setTechSpecific(newBits); } return true; } } /** * This class displays a dialog for handling "global" node properties. */ private static class GlobalPropertiesDialog extends EDialog { private JTextField value; private JComboBox combo; private boolean cancelHit; private boolean moreHit; /** Creates new special node properties object for "global"s */ private GlobalPropertiesDialog(EditWindow wnd, NodeInst ni) { super(null, true); String gName = ""; Variable var = ni.getVar(Schematics.SCHEM_GLOBAL_NAME); if (var != null) gName = var.getObject().toString(); initComponents(wnd, ni, "Global Signal", gName); cancelHit = moreHit = false; setVisible(true); // all done: see if value should be updated if (!cancelHit) { String newValue = value.getText(); PortCharacteristic ch = PortCharacteristic.findCharacteristic((String)combo.getSelectedItem()); int newBits = ch.getBits(); new ModifyNodeProperties(ni, Schematics.SCHEM_GLOBAL_NAME, newValue, newBits); } } protected void escapePressed() { exit(false); } private boolean wantMore() { return moreHit; } private void exit(boolean goodButton) { cancelHit = !goodButton; setVisible(false); dispose(); } private void moreButton() { moreHit = true; exit(true); } private void initComponents(EditWindow wnd, NodeInst ni, String title, String initialValue) { getContentPane().setLayout(new GridBagLayout()); setTitle(title); setName(""); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { exit(false); } }); // global information JLabel lab1 = new JLabel("Global signal name:"); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(lab1, gbc); value = new JTextField(initialValue); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 0; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = .5; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(value, gbc); EDialog.makeTextFieldSelectAllOnTab(value); value.selectAll(); JLabel lab2 = new JLabel("Characteristics:"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(lab2, gbc); combo = new JComboBox(); List<PortCharacteristic> characteristics = PortCharacteristic.getOrderedCharacteristics(); for(PortCharacteristic ch : characteristics) combo.addItem(ch.getName()); PortCharacteristic ch = PortCharacteristic.findCharacteristic(ni.getTechSpecific()); combo.setSelectedItem(ch.getName()); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 1; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = .5; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(combo, gbc); // OK, More, and Cancel JButton cancel = new JButton("Cancel"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(cancel, gbc); cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(false); } }); JButton more = new JButton("More..."); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(more, gbc); more.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { moreButton(); } }); JButton ok = new JButton("OK"); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 2; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(ok, gbc); ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(true); } }); getRootPane().setDefaultButton(ok); pack(); // now make the dialog appear over a node Point ew = wnd.getLocationOnScreen(); Point locInWnd = wnd.databaseToScreen(ni.getAnchorCenterX(), ni.getAnchorCenterY()); Point textfield = value.getLocation(); Dimension textSize = value.getSize(); setLocation(locInWnd.x+ew.x-(textfield.x+textSize.width/2), locInWnd.y+ew.y-(textfield.y+textSize.height/2+20)); } } /** * This class displays a dialog for handling "transistor" node properties. */ private static class TransistorPropertiesDialog extends EDialog { private JTextField valueWid, valueLen; private JComboBox comboWid, comboLen; private boolean cancelHit; private boolean moreHit; /** Creates new special node properties object for transistors */ private TransistorPropertiesDialog(EditWindow wnd, NodeInst ni) { super(null, true); String tWid = ""; CodeExpression.Code cWid = CodeExpression.Code.NONE; Variable varWid = ni.getVar(Schematics.ATTR_WIDTH); if (varWid != null) { tWid = varWid.getObject().toString(); cWid = varWid.getCode(); } String tLen = ""; CodeExpression.Code cLen = CodeExpression.Code.NONE; Variable varLen = ni.getVar(Schematics.ATTR_LENGTH); if (varLen != null) { tLen = varLen.getObject().toString(); cLen = varLen.getCode(); } initComponents(wnd, ni, "Transistor Properties", tWid, cWid, tLen, cLen); cancelHit = moreHit = false; setVisible(true); // all done: see if value should be updated if (!cancelHit) { String newWid = valueWid.getText(); String newLen = valueLen.getText(); CodeExpression.Code newWidCode = (CodeExpression.Code)comboWid.getSelectedItem(); CodeExpression.Code newLenCode = (CodeExpression.Code)comboLen.getSelectedItem(); new ModifyNodeProperties(ni, newWid, newWidCode, newLen, newLenCode); } } protected void escapePressed() { exit(false); } private boolean wantMore() { return moreHit; } private void exit(boolean goodButton) { cancelHit = !goodButton; setVisible(false); dispose(); } private void moreButton() { moreHit = true; exit(true); } private void initComponents(EditWindow wnd, NodeInst ni, String title, String initialWid, CodeExpression.Code codeWid, String initialLen, CodeExpression.Code codeLen) { getContentPane().setLayout(new GridBagLayout()); setTitle(title); setName(""); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent evt) { exit(false); } }); // transistor width information JLabel lab1 = new JLabel("Width:"); 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); valueWid = new JTextField(initialWid); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 0; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(valueWid, gbc); EDialog.makeTextFieldSelectAllOnTab(valueWid); valueWid.selectAll(); comboWid = new JComboBox(); for(Iterator<CodeExpression.Code> it = CodeExpression.Code.getCodes(); it.hasNext(); ) comboWid.addItem(it.next()); comboWid.setSelectedItem(codeWid); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(comboWid, gbc); // transistor length information JLabel lab2 = new JLabel("Length:"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.anchor = GridBagConstraints.WEST; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(lab2, gbc); valueLen = new JTextField(initialLen); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(valueLen, gbc); EDialog.makeTextFieldSelectAllOnTab(valueLen); comboLen = new JComboBox(); for(Iterator<CodeExpression.Code> it = CodeExpression.Code.getCodes(); it.hasNext(); ) comboLen.addItem(it.next()); comboLen.setSelectedItem(codeLen); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 3; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(comboLen, gbc); // OK, More, and Cancel JButton cancel = new JButton("Cancel"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 4; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(cancel, gbc); cancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(false); } }); JButton more = new JButton("More..."); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 4; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(more, gbc); more.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { moreButton(); } }); JButton ok = new JButton("OK"); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 4; gbc.anchor = GridBagConstraints.CENTER; gbc.insets = new Insets(4, 4, 4, 4); getContentPane().add(ok, gbc); ok.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { exit(true); } }); getRootPane().setDefaultButton(ok); pack(); // now make the dialog appear over a node Point ew = wnd.getLocationOnScreen(); Point locInWnd = wnd.databaseToScreen(ni.getAnchorCenterX(), ni.getAnchorCenterY()); Point textfield = valueWid.getLocation(); Dimension textSize = valueWid.getSize(); setLocation(locInWnd.x+ew.x-(textfield.x+textSize.width/2), locInWnd.y+ew.y-(textfield.y+textSize.height/2+20)); } } }