/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: StatusBar.java
*
* Copyright (c) 2003 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.ui;
import com.sun.electric.Main;
import com.sun.electric.database.change.DatabaseChangeEvent;
import com.sun.electric.database.change.DatabaseChangeListener;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.PrimitiveNodeSize;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Client;
import com.sun.electric.tool.user.Highlight;
import com.sun.electric.tool.user.HighlightListener;
import com.sun.electric.tool.user.Highlighter;
import com.sun.electric.tool.user.UserInterfaceMain;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.border.BevelBorder;
/**
* This class manages the Electric status bar at the bottom of the edit window.
*/
public class StatusBar extends JPanel implements HighlightListener, DatabaseChangeListener
{
private static final long serialVersionUID = 1L;
private WindowFrame frame;
private String coords = null;
private String hierCoords = null;
private JLabel fieldSelected, fieldSize, fieldTech, fieldCoords, fieldHierCoords;
private boolean toBrowser = false;
private static String selectionOverride = null;
public StatusBar(WindowFrame frame, boolean browserDecide)
{
super(new GridBagLayout());
setBorder(new BevelBorder(BevelBorder.LOWERED));
this.frame = frame;
toBrowser = browserDecide;
fieldSelected = new JLabel();
addField(fieldSelected, 0, 0, 1, 0.0);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
add(new JSeparator(JSeparator.VERTICAL), gbc);
fieldSize = new JLabel();
addField(fieldSize, 2, 0, 1, 0.0);
gbc = new GridBagConstraints();
gbc.gridx = 3; gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
add(new JSeparator(JSeparator.VERTICAL), gbc);
fieldTech = new JLabel();
addField(fieldTech, 4, 0, 1, 0.0);
gbc = new GridBagConstraints();
gbc.gridx = 5; gbc.gridy = 0;
gbc.fill = GridBagConstraints.VERTICAL;
add(new JSeparator(JSeparator.VERTICAL), gbc);
fieldCoords = new JLabel();
fieldCoords.setHorizontalAlignment(JLabel.RIGHT);
addField(fieldCoords, 6, 0, 1, 0.35);
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = 7;
add(new JSeparator(JSeparator.HORIZONTAL), gbc);
fieldHierCoords = new JLabel(" ");
fieldHierCoords.setHorizontalAlignment(JLabel.RIGHT);
addField(fieldHierCoords, 0, 2, 7, 0.0);
UserInterfaceMain.addDatabaseChangeListener(this);
}
private void addField(JComponent field, int x, int y, int width, double weight)
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x; gbc.gridy = y;
gbc.gridwidth = width;
gbc.weightx = weight;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.WEST;
int rightInsert = (Client.isOSMac()) ? 20 : 4;
gbc.insets = new Insets(0, 4, 0, rightInsert);
add(field, gbc);
}
/**
* Highlighter depends on MDI or SDI mode.
* @return the highlighter to use. May be null if none.
*/
private Highlighter getHighlighter()
{
//if (TopLevel.isMDIMode())
//{
// get current internal frame highlighter
EditWindow wnd = EditWindow.getCurrent();
if (wnd == null) return null;
return wnd.getHighlighter();
//}
//return frame.getContent().getHighlighter();
}
public static void setCoordinates(String coords, WindowFrame wf)
{
StatusBar sb = null;
//if (TopLevel.isMDIMode())
//{
sb = Main.getStatusBar();
//} else
//{
// sb = wf.getFrame().getStatusBar();
//}
sb.coords = coords;
sb.redoStatusBar();
}
public static void setHierarchicalCoordinates(String hierCoords, WindowFrame wf)
{
StatusBar sb = null;
//if (TopLevel.isMDIMode())
//{
sb = Main.getStatusBar();
//} else
//{
// sb = wf.getFrame().getStatusBar();
//}
sb.hierCoords = hierCoords;
sb.redoStatusBar();
}
public static void setSelectionOverride(String ov)
{
selectionOverride = ov;
updateStatusBar();
}
public void highlightChanged(Highlighter which)
{
updateSelectedText();
}
/**
* Called when by a Highlighter when it loses focus. The argument
* is the Highlighter that has gained focus (may be null).
* @param highlighterGainedFocus the highlighter for the current window (may be null).
*/
public void highlighterLostFocus(Highlighter highlighterGainedFocus) {}
/**
* Method to update the status bar from current values.
* Call this when any of those values change.
*/
public static void updateStatusBar()
{
//if (TopLevel.isMDIMode())
//{
// StatusBar sb = TopLevel.getCurrentJFrame().getStatusBar();
// sb.redoStatusBar();
//} else
//{
// for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
// {
// WindowFrame wf = it.next();
// StatusBar sb = wf.getFrame().getStatusBar();
// if (sb != null)
// sb.redoStatusBar();
// }
//}
}
private void redoStatusBar()
{
updateSelectedText();
Cell cell = null;
WindowFrame thisFrame = frame;
if (thisFrame == null)
{
thisFrame = WindowFrame.getCurrentWindowFrame(false);
if (thisFrame != null) cell = thisFrame.getContent().getCell();
} else
{
Cell cellInPanel = thisFrame.getContent().getCell();
if (cellInPanel != null) cell = cellInPanel;
}
String sizeMsg = "";
if (cell != null)
{
if (thisFrame.getContent() instanceof TextWindow)
{
TextWindow tw = (TextWindow)thisFrame.getContent();
int len = tw.getLineCount();
sizeMsg = "LINES: " + len;
} else
{
Rectangle2D bounds = cell.getBounds();
sizeMsg = "SIZE: " + TextUtils.formatDistance(bounds.getWidth(), cell.getTechnology()) + " x " +
TextUtils.formatDistance(bounds.getHeight(), cell.getTechnology());
}
}
fieldSize.setText(sizeMsg = " " + sizeMsg + " ");
fieldSize.setToolTipText(sizeMsg);
Technology tech = Technology.getCurrent();
if (tech != null)
{
String message = "TECH: " + tech.getTechName();
String foundry = tech.getPrefFoundry();
boolean validFoundry = !foundry.equals("");
if (tech.isScaleRelevant())
{
message += " (scale=" + tech.getScale() + "nm";
if (!validFoundry) message += ")";
else // relevant foundry
message += ",foundry=" + foundry + ")";
}
fieldTech.setText(" " + message + " ");
fieldTech.setToolTipText(message);
}
// // Determing if size and tech labels should appear
// FontMetrics fm = this.getGraphics().getFontMetrics(fieldSelected.getFont());
// int a = SwingUtilities.computeStringWidth(fm, fieldSelected.getText());
// Rectangle rectSel = fieldSelected.getBounds();
// Rectangle rectSize = fieldSize.getBounds();
// boolean visibleSize = (rectSel.getMinX() + a) < rectSize.getMinX();
// if (!visibleSize) fieldSize.setText("");
// Rectangle rectTech = fieldTech.getBounds();
// boolean visibleTech = (rectSel.getMinX() + a) < (rectTech.getMinX());
// if (!visibleTech) fieldTech.setText("");
if (coords == null) fieldCoords.setText(""); else
{
fieldCoords.setText(coords);
fieldCoords.setToolTipText(coords);
}
// if too many chars to display in space provided, truncate.
if (hierCoords != null)
{
int width = fieldHierCoords.getFontMetrics(fieldHierCoords.getFont()).stringWidth(hierCoords);
int widgetW = fieldHierCoords.getWidth();
if (width > widgetW)
{
int chars = hierCoords.length() * widgetW / width;
hierCoords = hierCoords.substring(hierCoords.length() - chars, hierCoords.length());
}
fieldHierCoords.setText(hierCoords);
} else
fieldHierCoords.setText(" ");
}
private void updateSelectedText()
{
String selectedMsg = "NOTHING SELECTED";
if (selectionOverride != null)
{
selectedMsg = selectionOverride;
} else
{
// count the number of nodes and arcs selected
int nodeCount = 0, arcCount = 0, textCount = 0;
Highlight lastHighlight = null;
Highlighter highlighter = getHighlighter();
if (highlighter == null) {
fieldSelected.setText(" " + selectedMsg + " ");
fieldSelected.setToolTipText(selectedMsg);
return;
}
NodeInst theNode = null;
for(Highlight h : highlighter.getHighlights())
{
if (!h.isValid()) continue;
if (h.isHighlightEOBJ())
{
ElectricObject eObj = h.getElectricObject();
if (eObj instanceof PortInst)
{
lastHighlight = h;
theNode = ((PortInst)eObj).getNodeInst();
nodeCount++;
} else if (eObj instanceof NodeInst)
{
lastHighlight = h;
theNode = (NodeInst)eObj;
nodeCount++;
} else if (eObj instanceof ArcInst)
{
lastHighlight = h;
arcCount++;
}
} else if (h.isHighlightText())
{
lastHighlight = h;
textCount++;
}
}
if (nodeCount + arcCount + textCount == 1)
{
selectedMsg = "SELECTED "+getSelectedText(lastHighlight);
if (theNode != null)
{
PrimitiveNodeSize npSize = theNode.getPrimitiveDependentNodeSize(null);
if (npSize != null)
{
selectedMsg += " (size=";
selectedMsg += npSize.getWidthInString();
selectedMsg += "x";
selectedMsg += npSize.getLengthInString();
selectedMsg += ")";
} else
{
double xSize = theNode.getLambdaBaseXSize();
double ySize = theNode.getLambdaBaseYSize();
selectedMsg += " (size=" + TextUtils.formatDistance(xSize, theNode.getProto().getTechnology()) +
" x " + TextUtils.formatDistance(ySize, theNode.getProto().getTechnology()) + ")";
}
}
} else
{
if (nodeCount + arcCount + textCount > 0)
{
StringBuffer buf = new StringBuffer();
buf.append("SELECTED:");
if (nodeCount > 0) buf.append(" " + nodeCount + " NODES");
if (arcCount > 0)
{
if (nodeCount > 0) buf.append(",");
buf.append(" " + arcCount + " ARCS");
}
if (textCount > 0)
{
if (nodeCount + arcCount > 0) buf.append(",");
buf.append(" " + textCount + " TEXT");
}
// add on info for last highlight
buf.append(". LAST: "+getSelectedText(lastHighlight));
selectedMsg = buf.toString();
}
}
}
fieldSelected.setText(" " + selectedMsg + " ");
fieldSelected.setToolTipText(selectedMsg);
}
private String addLayerInfo(PortProto pp)
{
// if the port is on a generic primitive which can connect to everything, say so
PrimitivePort pRp = pp.getBasePort();
if (pRp.getParent().getTechnology().isUniversalConnectivityPort(pRp))
return " [ALL]";
String descr = "";
ArcProto [] cons = pp.getBasePort().getConnections();
boolean first = true;
for(int i=0; i<cons.length; i++)
{
ArcProto ap = cons[i];
if (ap.getTechnology() == Generic.tech()) continue;
if (first) descr += " ["; else descr += ",";
first = false;
descr += cons[i].getName();
}
if (!first) descr += "]";
return descr;
}
/**
* Get a String describing the Highlight, to display in the
* "Selected" part of the status bar.
*/
private String getSelectedText(Highlight h)
{
assert h.isValid();
PortInst thePort;
NodeInst theNode;
ArcInst theArc;
if (h.isHighlightEOBJ())
{
ElectricObject eObj = h.getElectricObject();
if (eObj instanceof PortInst)
{
thePort = (PortInst)eObj;
theNode = thePort.getNodeInst();
String desc = (theNode.isCellInstance())?addLayerInfo(thePort.getPortProto()):"";
return "NODE: " + theNode.describe(true) +
" PORT: \'" + thePort.getPortProto().getName() + "\'" + desc;
} else if (eObj instanceof NodeInst)
{
theNode = (NodeInst)eObj;
return("NODE: " + theNode.describe(true));
} else if (eObj instanceof ArcInst)
{
theArc = (ArcInst)eObj;
Netlist netlist = theArc.getParent().getNetlist();
if (netlist == null)
return("netlist exception! try again");
if (!theArc.isLinked())
return("netlist exception! try again. ArcIndex = -1");
Network net = netlist.getNetwork(theArc, 0);
String netMsg = (net != null) ? "NETWORK: "+net.describe(true)+ ", " : "";
return netMsg + "ARC: " + theArc.describe(true);
}
} else if (h.isHighlightText())
{
String descr = "TEXT: " + h.describe();
if (h.getVarKey() == Export.EXPORT_NAME && h.getElectricObject() instanceof Export)
{
Export e = (Export)h.getElectricObject();
descr += addLayerInfo(e.getOriginalPort().getPortProto());
}
return descr;
}
return null;
}
/**
* Call when done with this Object. Cleans up references to this object.
*/
public void finished()
{
if (frame.getContent().getHighlighter() != null)
Highlighter.removeHighlightListener(this);
UserInterfaceMain.removeDatabaseChangeListener(this);
}
public void databaseChanged(DatabaseChangeEvent e) {
redoStatusBar();
}
}