/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: SizeMismatchPane.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.ncc;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.ncc.basic.NccUtils;
import com.sun.electric.tool.ncc.result.PartReport;
import com.sun.electric.tool.ncc.result.SizeMismatch.LengthMismatch;
import com.sun.electric.tool.ncc.result.SizeMismatch.Mismatch;
import com.sun.electric.tool.ncc.result.SizeMismatch.WidthMismatch;
import com.sun.electric.tool.user.Highlighter;
class SizeMismatchPane extends JPanel implements HyperlinkListener, AdjustmentListener {
// constants
public static final int MAXROWS = 200;
private static final Border border = BorderFactory.createEmptyBorder();
private static final Insets insets = new Insets(0,0,0,0);
private static final int HALF_INF = Integer.MAX_VALUE/2;
// GUI variables
private Dimension dimErrCol, dimWidCol, dimLenCol;
private JScrollPane headScrPane;
private Color bkgndColor;
private Font font = new Font("Helvetica", Font.PLAIN, 12);
// data holders
private NccGuiInfo result;
private Mismatch[] mismatches;
private PartReport[][] parts;
public SizeMismatchPane(NccGuiInfo res) {
super(new BorderLayout());
result = res;
mismatches = result.getSizeMismatches().toArray(new Mismatch[0]);
int size = Math.min(mismatches.length, MAXROWS);
if (size == 0) return;
parts = new PartReport[size][2];
// compute max numbers to estimate column width
int errColWidth = 7, widColWidth = 3, lenColWidth = 3;
for (int i=0; i<size; i++) {
String err = NccUtils.round(mismatches[i].relErr()*100,1) + "";
errColWidth = Math.max(errColWidth, err.length());
String w1 = NccUtils.round(mismatches[i].minPart.getWidth(),2) + "";
String w2 = NccUtils.round(mismatches[i].maxPart.getWidth(),2) + "";
int wid = Math.max(w1.length(), w2.length());
widColWidth = Math.max(widColWidth, wid+1);
String l1 = NccUtils.round(mismatches[i].minPart.getLength(),2) + "";
String l2 = NccUtils.round(mismatches[i].maxPart.getLength(),2) + "";
int len = Math.max(l1.length(), l2.length());
lenColWidth = Math.max(lenColWidth, len+1);
}
dimErrCol = new Dimension(errColWidth*7, 20);
dimWidCol = new Dimension(widColWidth*7, 16);
dimLenCol = new Dimension(lenColWidth*7, 16);
// create scroll pane with rows
JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL);
Box topBox = new Box(BoxLayout.Y_AXIS);
topBox.setAlignmentY(TOP_ALIGNMENT);
sep.setMaximumSize(new Dimension(Integer.MAX_VALUE, 1));
int maxRowWid = 0;
for (int i=0; i<size; i++) {
// add a row and a separator below it
JPanel row = createRow(i);
maxRowWid = Math.max(maxRowWid, row.getPreferredSize().width + 16);
topBox.add(row);
topBox.add(Box.createVerticalStrut(2));
topBox.add(sep);
topBox.add(Box.createVerticalStrut(2));
}
JScrollPane bodyScrPane = new JScrollPane(topBox);
bodyScrPane.setBorder(border);
bodyScrPane.setAlignmentX(LEFT_ALIGNMENT);
bodyScrPane.setAlignmentY(TOP_ALIGNMENT);
bodyScrPane.getHorizontalScrollBar().addAdjustmentListener(this);
// fill main container with header panel, separator, and scroll pane
JPanel header = createRow(-1);
Dimension dim = header.getPreferredSize();
dim.width = maxRowWid;
header.setPreferredSize(dim);
Box headBox = new Box(BoxLayout.Y_AXIS);
headBox.add(header);
headBox.add(Box.createVerticalStrut(2));
headBox.add(sep);
headBox.add(Box.createVerticalStrut(2));
headScrPane = new JScrollPane(headBox,
ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
headScrPane.setBorder(border);
headScrPane.setAlignmentX(LEFT_ALIGNMENT);
headScrPane.setAlignmentY(TOP_ALIGNMENT);
add(headScrPane, BorderLayout.NORTH);
add(bodyScrPane, BorderLayout.CENTER);
setBorder(border);
JScrollBar scrBar = bodyScrPane.getVerticalScrollBar();
scrBar.invalidate();
scrBar.validate();
scrBar.setValue(scrBar.getMinimum());
}
private JPanel createRow(int rowNdx) {
// create the main container of this row
JPanel row = new JPanel(new BorderLayout());
String relErr;
if (rowNdx < 0) {
bkgndColor = row.getBackground();
relErr = "Error,%";
} else {
bkgndColor = Color.WHITE;
row.setBackground(bkgndColor);
int firstCellNdx = 0;
if (result.isSwapCells()) firstCellNdx = 1;
if (mismatches[rowNdx].minNdx == firstCellNdx) {
parts[rowNdx][0] = mismatches[rowNdx].minPart;
parts[rowNdx][1] = mismatches[rowNdx].maxPart;
} else {
parts[rowNdx][1] = mismatches[rowNdx].minPart;
parts[rowNdx][0] = mismatches[rowNdx].maxPart;
}
if (mismatches[rowNdx].relErr()*100<.1)
relErr = "< 0.01";
else
relErr = NccUtils.round(mismatches[rowNdx].relErr()*100, 1) + "";
}
// create panel with relative error value
JLabel errLabel = new JLabel(relErr);
errLabel.setHorizontalAlignment(SwingConstants.RIGHT);
errLabel.setMinimumSize(dimErrCol);
errLabel.setMaximumSize(dimErrCol);
errLabel.setPreferredSize(dimErrCol);
errLabel.setFont(font);
errLabel.setBorder(border);
// set up the panel with error label and curcuit-specific data
JPanel errPanel = new JPanel();
errPanel.setLayout(new BoxLayout(errPanel, BoxLayout.X_AXIS));
errPanel.add(Box.createHorizontalStrut(4));
errPanel.add(errLabel);
errPanel.add(Box.createHorizontalStrut(4));
errPanel.add(new JSeparator(SwingConstants.VERTICAL));
errPanel.add(Box.createHorizontalStrut(2));
errPanel.setBackground(bkgndColor);
// set up the panel with two rows: one per curcuit
Box subRowsPanel = new Box(BoxLayout.Y_AXIS);
Dimension paramDims[] = {dimWidCol, dimLenCol};
String params[] = new String[2];
for (int line=0; line<2; line++) {
String name;
if (rowNdx < 0) {
params[0] = "Wid";
params[1] = "Len";
String titles[] = result.getNames();
name = "Name in " + titles[line];
} else {
params[0] = NccUtils.round(parts[rowNdx][line].getWidth(),2) + "";
params[1] = NccUtils.round(parts[rowNdx][line].getLength(),2) + "";
name = parts[rowNdx][line].instanceDescription();
}
JPanel subRow = createSubRow(params, paramDims, name, rowNdx, line);
subRow.setBackground(bkgndColor);
subRowsPanel.add(subRow);
}
// add numeric pane to the main container
row.add(errPanel, BorderLayout.WEST);
row.add(subRowsPanel, BorderLayout.CENTER);
// restrict height of the row
Dimension dim = new Dimension(row.getPreferredSize());
dim.width = HALF_INF;
row.setMaximumSize(dim);
row.setAlignmentX(LEFT_ALIGNMENT);
row.setAlignmentY(TOP_ALIGNMENT);
return row;
}
private JPanel createSubRow(String params[], Dimension paramDims[],
String name, int rowNdx, int lineNdx) {
//StringBuffer text = new StringBuffer(64);
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
panel.setBorder(border);
panel.setBackground(bkgndColor);
for (int i=0; i<2; i++) {
JLabel label = new JLabel(params[i]);
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setFont(font);
if (rowNdx >= 0) { // if not header
boolean red =
(i == 0 && mismatches[rowNdx] instanceof WidthMismatch) ||
(i == 1 && mismatches[rowNdx] instanceof LengthMismatch);
if (red)
label.setForeground(Color.RED);
}
label.setBorder(border);
label.setMinimumSize(paramDims[i]);
label.setMaximumSize(paramDims[i]);
label.setPreferredSize(paramDims[i]);
if (rowNdx >= 0) {
label.setAlignmentY(BOTTOM_ALIGNMENT);
label.setVerticalAlignment(SwingConstants.BOTTOM);
label.setVerticalTextPosition(SwingConstants.BOTTOM);
}
panel.add(label);
}
panel.add(Box.createHorizontalStrut(10));
panel.add(createNamePane(name, rowNdx, lineNdx));
return panel;
}
private JComponent createNamePane(String name, int rowNdx, int lineNdx) {
StringBuffer text = new StringBuffer(128);
String href = "<a style=\"text-decoration: none;\" href=\"";
text.append("<html><font size=3><font face=\"Helvetica, TimesRoman\">");
if (rowNdx < 0) { // used for header
JLabel label = new JLabel(name);
label.setFont(font);
label.setBorder(border);
return label;
}
// drop "Part:" or "Wire:" prefices
if (name.startsWith("Wire: ") || name.startsWith("Part: "))
name = name.substring(6);
// drop "Cell instance:" info
int ind = name.indexOf(" Cell instance:");
if (ind > 0) name = name.substring(0, ind).trim();
// drop {sch} or {lay} suffices
if (name.endsWith("{sch}") || name.endsWith("{lay}"))
name = name.substring(0, name.length()-5);
text.append(href + (rowNdx*10+lineNdx) +"\">"+ name +"</a>");
text.append("</font></html>");
JEditorPane pane = new JEditorPane();
pane.setBackground(bkgndColor);
pane.setEditable(false);
pane.addHyperlinkListener(this);
pane.setContentType("text/html");
pane.setText(text.toString());
pane.setMargin(insets);
pane.setBorder(border);
pane.moveCaretPosition(0);
return pane;
}
public void hyperlinkUpdate(HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
highlight(Integer.parseInt(event.getDescription()));
}
}
public void adjustmentValueChanged(AdjustmentEvent e) {
headScrPane.getHorizontalScrollBar().setValue(e.getValue());
}
private void highlight(int index) {
int row = index/10;
int col = index%2;
PartReport part = parts[row][col];
Cell cell = part.getNameProxy().leafCell();
VarContext context = part.getNameProxy().getContext();
// find the highlighter corresponding to the cell
Highlighter highlighter = HighlightTools.getHighlighter(cell, context);
if (highlighter == null) return;
HighlightTools.highlightPart(highlighter, cell, part);
highlighter.finished();
}
}