/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: WaveformWindow.java
*
* Copyright (c) 2004, 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.waveform;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.network.Global;
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.Pref;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.variable.CodeExpression;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.io.ExecProcess;
import com.sun.electric.tool.io.FileType;
import com.sun.electric.tool.io.input.SimulationData;
import com.sun.electric.tool.io.output.PNG;
import com.sun.electric.tool.io.output.Spice;
import com.sun.electric.tool.ncc.NccCrossProbing;
import com.sun.electric.tool.ncc.result.NccResult;
import com.sun.electric.tool.simulation.DigitalSample;
import com.sun.electric.tool.simulation.Sample;
import com.sun.electric.tool.simulation.ScalarSample;
import com.sun.electric.tool.simulation.Signal;
import com.sun.electric.tool.simulation.SignalCollection;
import com.sun.electric.tool.simulation.SimulationTool;
import com.sun.electric.tool.simulation.Stimuli;
import com.sun.electric.tool.user.ActivityLogger;
import com.sun.electric.tool.user.HighlightListener;
import com.sun.electric.tool.user.Highlighter;
import com.sun.electric.tool.user.Resources;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.dialogs.OpenFile;
import com.sun.electric.tool.user.ui.EditWindow;
import com.sun.electric.tool.user.ui.ElectricPrinter;
import com.sun.electric.tool.user.ui.ExplorerTree;
import com.sun.electric.tool.user.ui.ExplorerTreeModel;
import com.sun.electric.tool.user.ui.WindowContent;
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.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.FontRenderContext;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.PrinterJob;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
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.Stack;
import java.util.prefs.Preferences;
import javax.print.attribute.standard.ColorSupported;
import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.TableModelEvent;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
/**
* This class defines the a screenful of Panels that make up a waveform display.
*/
public class WaveformWindow implements WindowContent, PropertyChangeListener
{
/** minimum height of a panel */ private static final int MINPANELHEIGHT = 24;
/** the window that this lives in */ private WindowFrame wf;
/** the cell being simulated */ private Stimuli sd;
/** the signal on all X axes (null for time) */ private Signal<?> xAxisSignalAll;
/** the top-level panel of the waveform window. */ private JPanel overall;
/** the "lock X axis" button. */ private JButton xAxisLockButton;
/** the "refresh" button. */ private JButton refresh;
/** the "show points" button. */ private JButton showPoints;
/** the "grow panel" button for widening. */ private JButton growPanel;
/** the "shrink panel" button for narrowing. */ private JButton shrinkPanel;
/** the list of panels. */ private JComboBox signalNameList;
/** mapping from Signals to entries in "SIGNALS" tree*/ private Map<Signal<?>,TreePath> treePathFromSignal = new HashMap<Signal<?>,TreePath>();
/** true if rebuilding the list of panels */ private boolean rebuildingSignalNameList = false;
/** the main scroll of all panels. */ private JScrollPane scrollAll;
/** left panel: the signal names */ private JPanel left;
/** right panel: the signal traces */ private JPanel right;
/** the table with panels and labels */ private WaveTable table;
/** the table editor for left and right halves */ private WaveCellEditor leftSideColumn, rightSideColumn;
/** the table with panels and labels */ private TableModel tableModel;
/** labels for the text at the top */ private JLabel mainPos, extPos, delta, diskLabel;
/** buttons for centering the X-axis cursors. */ private JButton centerMain, centerExt;
/** a list of panels in this window */ private List<Panel> wavePanels;
/** a list of sweep signals in this window */ private Map<String,SweepSignal[]> sweepSignals;
/** the main horizontal ruler for all panels. */ private HorizRuler mainHorizRulerPanel;
/** true if the main horizontal ruler is logarithmic */ private boolean mainHorizRulerPanelLogarithmic;
/** the VCR timer, when running */ private Timer vcrTimer;
/** true to run VCR backwards */ private boolean vcrPlayingBackwards = false;
/** time the VCR last advanced */ private long vcrLastAdvance;
/** speed of the VCR (in screen pixels) */ private int vcrAdvanceSpeed = 3;
/** current "main" x-axis cursor */ private double mainXPosition;
/** current "extension" x-axis cursor */ private double extXPosition;
/** default range along horizontal axis */ private double minXPosition, maxXPosition;
/** true if the X axis is the same in each panel */ private boolean xAxisLocked;
/** the sweep signal that is highlighted */ private int highlightedSweep = -1;
/** display mode (0=lines, 1=lines&points, 2=points) */ private int linePointMode;
/** true to show a grid */ private boolean showGrid;
/** the actual screen coordinates of the waveform */ private int screenLowX, screenHighX;
/** a listener for redraw requests */ private WaveComponentListener wcl;
/** The highlighter for this waveform window. */ private Highlighter highlighter;
/** 0: color display, 1: color printing, 2: B&W printing */ private int nowPrinting;
/** lock for crossprobing */ private boolean freezeWaveformHighlighting = false;
/** lock for crossprobing */ private boolean freezeEditWindowHighlighting = false;
/** The global listener for all waveform windows. */ private WaveformWindowHighlightListener waveHighlighter = new WaveformWindowHighlightListener();
/** Font for all text in the window */ private Font waveWindowFont;
/** For rendering text */ private FontRenderContext waveWindowFRC;
/** The colors of signal lines */ private Color offStrengthColor, nodeStrengthColor, gateStrengthColor, powerStrengthColor;
/** The background color */ private Color backgroundColor;
/** drop target (for drag and drop) */ public WaveFormDropTarget waveformDropTarget = new WaveFormDropTarget();
private static final ImageIcon iconAddPanel = Resources.getResource(WaveformWindow.class, "ButtonSimAddPanel.gif");
private static final ImageIcon iconLockXAxes = Resources.getResource(WaveformWindow.class, "ButtonSimLockTime.gif");
private static final ImageIcon iconUnLockXAxes = Resources.getResource(WaveformWindow.class, "ButtonSimUnLockTime.gif");
private static final ImageIcon iconRefresh = Resources.getResource(WaveformWindow.class, "ButtonSimRefresh.gif");
private static final ImageIcon iconLineOnPointOn = Resources.getResource(WaveformWindow.class, "ButtonSimLineOnPointOn.gif");
private static final ImageIcon iconLineOnPointOff = Resources.getResource(WaveformWindow.class, "ButtonSimLineOnPointOff.gif");
private static final ImageIcon iconLineOffPointOn = Resources.getResource(WaveformWindow.class, "ButtonSimLineOffPointOn.gif");
private static final ImageIcon iconToggleGrid = Resources.getResource(WaveformWindow.class, "ButtonSimGrid.gif");
private static final ImageIcon iconGrowPanel = Resources.getResource(WaveformWindow.class, "ButtonSimGrow.gif");
private static final ImageIcon iconShrinkPanel = Resources.getResource(WaveformWindow.class, "ButtonSimShrink.gif");
private static final ImageIcon iconVCRRewind = Resources.getResource(WaveformWindow.class, "ButtonVCRRewind.gif");
private static final ImageIcon iconVCRPlayBackward = Resources.getResource(WaveformWindow.class, "ButtonVCRPlayBackward.gif");
private static final ImageIcon iconVCRStop = Resources.getResource(WaveformWindow.class, "ButtonVCRStop.gif");
private static final ImageIcon iconVCRPlay = Resources.getResource(WaveformWindow.class, "ButtonVCRPlay.gif");
private static final ImageIcon iconVCRToEnd = Resources.getResource(WaveformWindow.class, "ButtonVCRToEnd.gif");
private static final ImageIcon iconVCRFaster = Resources.getResource(WaveformWindow.class, "ButtonVCRFaster.gif");
private static final ImageIcon iconVCRSlower = Resources.getResource(WaveformWindow.class, "ButtonVCRSlower.gif");
private static final Cursor resizeRowCursor = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
private static final Cursor resizeColumnCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
/**
* Constructor creates a Waveform window in a given WindowFrame with given Stimuli data.
* @param sd the Stimuli data to show in the window.
* @param wf the WindowFrame in which to place the window.
*/
public WaveformWindow(Stimuli sd, WindowFrame wf)
{
// initialize the structure
this.wf = wf;
this.sd = sd;
sd.setWaveformWindow(this);
resetSweeps();
wavePanels = new ArrayList<Panel>();
xAxisLocked = true;
linePointMode = 0;
nowPrinting = 0;
showGrid = false;
xAxisSignalAll = null;
mainHorizRulerPanelLogarithmic = false;
// compute fields used in graphics
waveWindowFont = new Font(User.getDefaultFont(), Font.PLAIN, 12);
waveWindowFRC = new FontRenderContext(null, false, false);
offStrengthColor = new Color(User.getColor(User.ColorPrefType.WAVE_OFF_STRENGTH));
nodeStrengthColor = new Color(User.getColor(User.ColorPrefType.WAVE_NODE_STRENGTH));
gateStrengthColor = new Color(User.getColor(User.ColorPrefType.WAVE_GATE_STRENGTH));
powerStrengthColor = new Color(User.getColor(User.ColorPrefType.WAVE_POWER_STRENGTH));
highlighter = new Highlighter(Highlighter.SELECT_HIGHLIGHTER, wf);
Highlighter.addHighlightListener(waveHighlighter);
// the total panel in the waveform window
overall = new OnePanel(null, this);
overall.setLayout(new GridBagLayout());
wcl = new WaveComponentListener(overall);
overall.addComponentListener(wcl);
// a drop target for the overall waveform window
new DropTarget(overall, DnDConstants.ACTION_LINK, waveformDropTarget, true);
// the table that holds the waveform panels
tableModel = new WaveTableModel();
table = new WaveTable(tableModel, this);
new TableMouseListener(table);
table.getTableHeader().setPreferredSize(new Dimension(1, 1));
TableColumn column1 = table.getColumnModel().getColumn(0);
column1.setPreferredWidth(100);
TableColumn column2 = table.getColumnModel().getColumn(1);
column2.setPreferredWidth(500);
leftSideColumn = new WaveCellEditor(table, 0);
rightSideColumn = new WaveCellEditor(table, 1);
int height = User.getWaveformDigitalPanelHeight();
if (sd.isAnalog()) height = User.getWaveformAnalogPanelHeight();
table.setRowHeight(height);
scrollAll = new JScrollPane(table);
// a drop target for the table
new DropTarget(table, DnDConstants.ACTION_LINK, waveformDropTarget, true);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 2;
gbc.gridwidth = 11; gbc.gridheight = 1;
gbc.weightx = 0; gbc.weighty = 1;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
overall.add(scrollAll, gbc);
// the top part of the waveform window: status information
JButton addPanel = new JButton(iconAddPanel);
addPanel.setBorderPainted(false);
addPanel.setDefaultCapable(false);
addPanel.setToolTipText("Create new waveform panel");
Dimension minWid = new Dimension(iconAddPanel.getIconWidth()+4, iconAddPanel.getIconHeight()+4);
addPanel.setMinimumSize(minWid);
addPanel.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(addPanel, gbc);
addPanel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { makeNewPanel(-1); }
});
showPoints = new JButton(iconLineOnPointOff);
showPoints.setBorderPainted(false);
showPoints.setDefaultCapable(false);
showPoints.setToolTipText("Toggle display of vertex points and lines");
minWid = new Dimension(iconLineOnPointOff.getIconWidth()+4, iconLineOnPointOff.getIconHeight()+4);
showPoints.setMinimumSize(minWid);
showPoints.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(showPoints, gbc);
showPoints.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { toggleShowPoints(); }
});
JButton toggleGrid = new JButton(iconToggleGrid);
toggleGrid.setBorderPainted(false);
toggleGrid.setDefaultCapable(false);
toggleGrid.setToolTipText("Toggle display of a grid");
minWid = new Dimension(iconToggleGrid.getIconWidth()+4, iconToggleGrid.getIconHeight()+4);
toggleGrid.setMinimumSize(minWid);
toggleGrid.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(toggleGrid, gbc);
toggleGrid.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { toggleGridPoints(); }
});
refresh = new JButton(iconRefresh);
refresh.setBorderPainted(false);
refresh.setDefaultCapable(false);
refresh.setToolTipText("Reread stimuli data file and update waveforms");
minWid = new Dimension(iconRefresh.getIconWidth()+4, iconRefresh.getIconHeight()+4);
refresh.setMinimumSize(minWid);
refresh.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(refresh, gbc);
refresh.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { refreshData(); }
});
xAxisLockButton = new JButton(iconLockXAxes);
xAxisLockButton.setBorderPainted(false);
xAxisLockButton.setDefaultCapable(false);
xAxisLockButton.setToolTipText("Lock all panels horizontally");
minWid = new Dimension(iconLockXAxes.getIconWidth()+4, iconLockXAxes.getIconHeight()+4);
xAxisLockButton.setMinimumSize(minWid);
xAxisLockButton.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(xAxisLockButton, gbc);
xAxisLockButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { togglePanelXAxisLock(); }
});
signalNameList = new JComboBox();
signalNameList.setToolTipText("Show or hide waveform panels");
signalNameList.setLightWeightPopupEnabled(false);
gbc = new GridBagConstraints();
gbc.gridx = 3; gbc.gridy = 0;
gbc.gridwidth = 5; gbc.gridheight = 1;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 0, 0, 0);
overall.add(signalNameList, gbc);
signalNameList.addItem("Panel 1");
signalNameList.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { togglePanelName(); }
});
growPanel = new JButton(iconGrowPanel);
growPanel.setBorderPainted(false);
growPanel.setDefaultCapable(false);
growPanel.setToolTipText("Increase minimum panel height");
minWid = new Dimension(iconGrowPanel.getIconWidth()+4, iconGrowPanel.getIconHeight()+4);
growPanel.setMinimumSize(minWid);
growPanel.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 8; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(growPanel, gbc);
growPanel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { growPanels(1.25); }
});
shrinkPanel = new JButton(iconShrinkPanel);
shrinkPanel.setBorderPainted(false);
shrinkPanel.setDefaultCapable(false);
shrinkPanel.setToolTipText("Decrease minimum panel height");
minWid = new Dimension(iconShrinkPanel.getIconWidth()+4, iconShrinkPanel.getIconHeight()+4);
shrinkPanel.setMinimumSize(minWid);
shrinkPanel.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 9; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(shrinkPanel, gbc);
shrinkPanel.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { growPanels(0.8); }
});
// the X axis section that shows the value of the main and extension cursors
JPanel xAxisLabelPanel = new JPanel();
xAxisLabelPanel.setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.gridx = 10; gbc.gridy = 0;
gbc.weightx = 1; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 4, 0, 4);
overall.add(xAxisLabelPanel, gbc);
mainPos = new JLabel("Main:", JLabel.RIGHT);
mainPos.setToolTipText("The main (dashed) X axis cursor");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.weightx = 0.2; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 0, 0, 0);
xAxisLabelPanel.add(mainPos, gbc);
centerMain = new JButton("Center");
centerMain.setToolTipText("Center the main (dashed) X axis cursor");
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(2, 4, 2, 0);
xAxisLabelPanel.add(centerMain, gbc);
centerMain.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { centerCursor(true); }
});
extPos = new JLabel("Ext:", JLabel.RIGHT);
extPos.setToolTipText("The extension (dotted) X axis cursor");
gbc = new GridBagConstraints();
gbc.gridx = 2; gbc.gridy = 0;
gbc.weightx = 0.2; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 0, 0, 0);
xAxisLabelPanel.add(extPos, gbc);
centerExt = new JButton("Center");
centerExt.setToolTipText("Center the extension (dotted) X axis cursor");
gbc = new GridBagConstraints();
gbc.gridx = 3; gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(2, 4, 2, 0);
xAxisLabelPanel.add(centerExt, gbc);
centerExt.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { centerCursor(false); }
});
delta = new JLabel("Delta:", JLabel.CENTER);
delta.setToolTipText("X distance between cursors");
gbc = new GridBagConstraints();
gbc.gridx = 4; gbc.gridy = 0;
gbc.weightx = 0.2; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 0, 0, 0);
xAxisLabelPanel.add(delta, gbc);
// the name of the waveform disk file
if (sd.getFileURL() != null)
{
String fileName = TextUtils.getFileNameWithoutExtension(sd.getFileURL());
String ext = TextUtils.getExtension(sd.getFileURL());
if (ext.length() > 0) fileName += "." + ext;
diskLabel = new JLabel("File: " + fileName, JLabel.CENTER);
diskLabel.setToolTipText("The disk file that is being displayed");
gbc = new GridBagConstraints();
gbc.gridx = 5; gbc.gridy = 0;
gbc.weightx = 0.4; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(0, 10, 0, 0);
xAxisLabelPanel.add(diskLabel, gbc);
}
// add VCR controls
JButton vcrButtonRewind = new JButton(iconVCRRewind);
vcrButtonRewind.setBorderPainted(false);
vcrButtonRewind.setDefaultCapable(false);
vcrButtonRewind.setToolTipText("Rewind main X axis cursor to start");
minWid = new Dimension(iconVCRRewind.getIconWidth()+4, iconVCRRewind.getIconHeight()+4);
vcrButtonRewind.setMinimumSize(minWid);
vcrButtonRewind.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 3; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonRewind, gbc);
vcrButtonRewind.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickRewind(); }
});
JButton vcrButtonPlayBackwards = new JButton(iconVCRPlayBackward);
vcrButtonPlayBackwards.setBorderPainted(false);
vcrButtonPlayBackwards.setDefaultCapable(false);
vcrButtonPlayBackwards.setToolTipText("Play main X axis cursor backwards");
minWid = new Dimension(iconVCRPlayBackward.getIconWidth()+4, iconVCRPlayBackward.getIconHeight()+4);
vcrButtonPlayBackwards.setMinimumSize(minWid);
vcrButtonPlayBackwards.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 4; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonPlayBackwards, gbc);
vcrButtonPlayBackwards.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickPlayBackwards(); }
});
JButton vcrButtonStop = new JButton(iconVCRStop);
vcrButtonStop.setBorderPainted(false);
vcrButtonStop.setDefaultCapable(false);
vcrButtonStop.setToolTipText("Stop moving main X axis cursor");
minWid = new Dimension(iconVCRStop.getIconWidth()+4, iconVCRStop.getIconHeight()+4);
vcrButtonStop.setMinimumSize(minWid);
vcrButtonStop.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 5; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonStop, gbc);
vcrButtonStop.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickStop(); }
});
JButton vcrButtonPlay = new JButton(iconVCRPlay);
vcrButtonPlay.setBorderPainted(false);
vcrButtonPlay.setDefaultCapable(false);
vcrButtonPlay.setToolTipText("Play main X axis cursor");
minWid = new Dimension(iconVCRPlay.getIconWidth()+4, iconVCRPlay.getIconHeight()+4);
vcrButtonPlay.setMinimumSize(minWid);
vcrButtonPlay.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 6; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonPlay, gbc);
vcrButtonPlay.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickPlay(); }
});
JButton vcrButtonToEnd = new JButton(iconVCRToEnd);
vcrButtonToEnd.setBorderPainted(false);
vcrButtonToEnd.setDefaultCapable(false);
vcrButtonToEnd.setToolTipText("Move main X axis cursor to end");
minWid = new Dimension(iconVCRToEnd.getIconWidth()+4, iconVCRToEnd.getIconHeight()+4);
vcrButtonToEnd.setMinimumSize(minWid);
vcrButtonToEnd.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 7; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonToEnd, gbc);
vcrButtonToEnd.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickToEnd(); }
});
JButton vcrButtonFaster = new JButton(iconVCRFaster);
vcrButtonFaster.setBorderPainted(false);
vcrButtonFaster.setDefaultCapable(false);
vcrButtonFaster.setToolTipText("Move main X axis cursor faster");
minWid = new Dimension(iconVCRFaster.getIconWidth()+4, iconVCRFaster.getIconHeight()+4);
vcrButtonFaster.setMinimumSize(minWid);
vcrButtonFaster.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 8; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonFaster, gbc);
vcrButtonFaster.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickFaster(); }
});
JButton vcrButtonSlower = new JButton(iconVCRSlower);
vcrButtonSlower.setBorderPainted(false);
vcrButtonSlower.setDefaultCapable(false);
vcrButtonSlower.setToolTipText("Move main X axis cursor slower");
minWid = new Dimension(iconVCRSlower.getIconWidth()+4, iconVCRSlower.getIconHeight()+4);
vcrButtonSlower.setMinimumSize(minWid);
vcrButtonSlower.setPreferredSize(minWid);
gbc = new GridBagConstraints();
gbc.gridx = 9; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.CENTER;
overall.add(vcrButtonSlower, gbc);
vcrButtonSlower.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { vcrClickSlower(); }
});
backgroundColor = vcrButtonSlower.getBackground();
// the single horizontal ruler panel (when the X axes are locked)
if (xAxisLocked)
{
addMainHorizRulerPanel();
}
// set bounds of the window from extent of the data
double lowTime = sd.getMinTime();
double highTime = sd.getMaxTime();
double timeRange = highTime - lowTime;
setMainXPositionCursor(timeRange*0.2 + lowTime);
setExtensionXPositionCursor(timeRange*0.8 + lowTime);
setDefaultHorizontalRange(lowTime, highTime);
}
private class WaveTable extends JTable
{
private WaveformWindow ww;
WaveTable(TableModel model, WaveformWindow ww)
{
super(model);
this.ww = ww;
}
}
private class WaveTableModel extends DefaultTableModel
{
public int getColumnCount() { return 2; }
public int getRowCount()
{
int numVisPanels = 0;
for(Panel wp : wavePanels)
{
if (!wp.isHidden()) numVisPanels++;
}
return numVisPanels;
}
public Object getValueAt(int row, int col)
{
int rowNo = 0;
for(Panel panel : wavePanels)
{
if (panel.isHidden()) continue;
if (rowNo == row)
{
if (col == 0) return panel.getLeftHalf();
return panel.getRightHalf();
}
rowNo++;
}
return null;
}
public void setValueAt(Object obj, int row, int col) {}
public Class<?> getColumnClass(int column)
{
return JPanel.class;
}
}
static class WaveCellEditor extends AbstractCellEditor
implements TableCellRenderer, TableCellEditor
{
private Component lastOne;
public WaveCellEditor(JTable table, int column)
{
super();
TableColumnModel columnModel = table.getColumnModel();
columnModel.getColumn(column).setCellRenderer(this);
columnModel.getColumn(column).setCellEditor(this);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
return (Component)table.getValueAt(row, column);
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
lastOne = (Component)table.getValueAt(row, column);
return lastOne;
}
public Object getCellEditorValue()
{
return lastOne;
}
}
private class TableMouseListener implements MouseListener, MouseMotionListener
{
private int mouseXOffset, mouseYOffset, resizingRow;
private TableColumn resizingColumn;
private JTable table;
public TableMouseListener(JTable table)
{
this.table = table;
table.addMouseListener(this);
table.addMouseMotionListener(this);
}
private TableColumn getResizingColumn(Point p)
{
int column = table.columnAtPoint(p);
if (column == -1) return null;
int row = table.rowAtPoint(p);
if (row == -1) return null;
Rectangle r = table.getCellRect(row, column, true);
r.grow(-3, 0);
if (r.contains(p)) return null;
int midPoint = r.x + r.width / 2;
int columnIndex = (p.x < midPoint) ? column - 1 : column;
if (columnIndex == -1) return null;
return table.getTableHeader().getColumnModel().getColumn(columnIndex);
}
private int getResizingRow(Point p)
{
int row = table.rowAtPoint(p);
if (row == -1) return -1;
int col = table.columnAtPoint(p);
if (col == -1) return -1;
Rectangle r = table.getCellRect(row, col, true);
r.grow(0, -3);
if (r.contains(p)) return -1;
int midPoint = r.y + r.height / 2;
int rowIndex = (p.y < midPoint) ? row - 1 : row;
return rowIndex;
}
public void mouseClicked(MouseEvent e)
{
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mouseEntered(MouseEvent e)
{
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mouseExited(MouseEvent e)
{
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mousePressed(MouseEvent e)
{
table.getTableHeader().setResizingColumn(null);
// figure out if a divider was hit
Point p = e.getPoint();
resizingColumn = getResizingColumn(p);
if (resizingColumn != null)
{
resizingRow = -1;
table.getTableHeader().setResizingColumn(resizingColumn);
mouseXOffset = p.x - resizingColumn.getWidth();
return;
}
resizingRow = getResizingRow(p);
if (resizingRow >= 0)
{
mouseYOffset = p.y - table.getRowHeight(resizingRow);
return;
}
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mouseMoved(MouseEvent e)
{
if (getResizingColumn(e.getPoint()) != null)
{
table.setCursor(resizeColumnCursor);
return;
}
if (getResizingRow(e.getPoint()) >= 0)
{
table.setCursor(resizeRowCursor);
return;
}
table.setCursor(null);
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mouseDragged(MouseEvent e)
{
if (resizingColumn != null)
{
resizingColumn.setWidth(e.getX() - mouseXOffset);
if (mainHorizRulerPanel != null) mainHorizRulerPanel.repaint();
return;
}
if (resizingRow >= 0)
{
int newHeight = e.getY() - mouseYOffset;
if (newHeight < MINPANELHEIGHT) newHeight = MINPANELHEIGHT;
table.setRowHeight(resizingRow, newHeight);
if (resizingRow < wavePanels.size())
{
Panel wp = wavePanels.get(resizingRow);
wp.updatePanelTitle();
}
return;
}
// forward event to the panel contents
forwardEventToPanel(e);
}
public void mouseReleased(MouseEvent e)
{
if (resizingColumn != null)
{
table.getTableHeader().setResizingColumn(null);
return;
}
// forward event to the panel contents
forwardEventToPanel(e);
}
public void forwardEventToPanel(MouseEvent e)
{
Point p = e.getPoint();
int row = table.rowAtPoint(p);
int col = table.columnAtPoint(p);
if (row < 0 || col < 0) return;
JPanel panel = (JPanel)table.getValueAt(row, col);
// so clicks are felt inside the panels
MouseEvent panelEvent = SwingUtilities.convertMouseEvent(table, e, panel);
panel.dispatchEvent(panelEvent);
// This is necessary so that when a button is pressed and released
// it gets rendered properly. Otherwise, the button may still appear
// pressed down when it has been released.
table.repaint();
}
}
public void stopEditing()
{
leftSideColumn.stopCellEditing();
rightSideColumn.stopCellEditing();
}
public void reloadTable()
{
table.tableChanged(new TableModelEvent(tableModel));
}
// ************************************* REQUIRED IMPLEMENTATION METHODS *************************************
/**
* Method to get rid of this WaveformWindow. Called by WindowFrame when
* that windowFrame gets closed.
*/
public void finished()
{
for(Panel wp : wavePanels)
{
wp.finished();
}
Highlighter.removeHighlightListener(waveHighlighter);
overall.removeComponentListener(wcl);
highlighter.delete();
if (sd != null)
sd.finished();
}
public void fullRepaint() { repaint(); }
public void repaint()
{
for(Panel wp : wavePanels)
{
wp.repaintContents();
}
if (mainHorizRulerPanel != null)
mainHorizRulerPanel.repaint();
}
public static void exportSimulationDataAsCSV(String file)
{
if (file == null) // cancel operation for example
return;
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow)) {
System.out.println("Must select a Waveform window first");
return;
}
WaveformWindow ww = (WaveformWindow)content;
try {
PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file)));
for(Panel wp : ww.wavePanels)
wp.dumpDataCSV(pw);
pw.close();
System.out.println("Exported Waveform in CSV format file '" + file + "'");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void plotSimulationData(String file, String format) {
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow)) {
System.out.println("Must select a Waveform window first");
return;
}
WaveformWindow ww = (WaveformWindow)content;
try {
String commands = "";
double min = Double.MAX_VALUE;
double max = -Double.MAX_VALUE;
int numPanels = 0;
int maxWidth = 0;
int height = 0;
for(Panel wp : ww.wavePanels) {
numPanels++;
min = Math.min(min, wp.convertXScreenToData(0));
max = Math.max(max, wp.convertXScreenToData(wp.getSz().width));
maxWidth = Math.max(wp.getSz().width, maxWidth);
height += wp.getSz().height;
}
System.out.println("plotting: maxWidth="+maxWidth+", height="+height);
if (file!=null) {
commands += "set terminal "+format+" size 9 , "+(((double)height*9)/maxWidth)+"; ";
commands += "set output \""+file+"\"; ";
} else {
commands += "set terminal aqua size "+(maxWidth+100)+" "+(height+100)+"; ";
}
commands += "unset colorbox; ";
commands += "set multiplot; ";
commands += "set xrange [\""+min+"\":\""+max+"\"]; ";
commands += "set format x \"\"; ";
System.out.println("Running: gnuplot for "+numPanels+" panels");
ExecProcess ep = new ExecProcess(new String[] { "gnuplot" }, null);
ep.redirectStdout(System.out);
ep.redirectStderr(System.out);
ep.start();
PrintWriter pw = new PrintWriter(new OutputStreamWriter(new ExecProcess.MultiOutputStream(new OutputStream[] { System.out, ep.getStdin() })));
pw.println(commands);
pw.println("set label 1 \"Voltage (Volts)\" at 1,1 left");
pw.println();
int whichPanel = 0;
double ypos = 1.0;
for(Panel wp : ww.wavePanels) {
if (whichPanel==numPanels-1) {
pw.println("set xlabel \"time (in seconds)\"; ");
pw.println("unset format; ");
}
pw.println("set size "+
(((double)wp.getSz().width)/maxWidth)+
","+
(((double)wp.getSz().height)/height)+"; ");
ypos -= (((double)wp.getSz().height)/height);
pw.println("set origin 0, "+ypos);
pw.flush();
pw.print("plot ");
wp.dumpDataForGnuplot(pw, min, max, ",");
pw.println();
pw.flush();
whichPanel++;
}
pw.println("unset multiplot; ");
pw.println("quit;");
pw.flush();
pw.close();
System.out.println("gnuplot finished.");
} catch (Exception e) {
System.out.println("ERROR: Unable to run 'gnuplot': " + e);
}
}
/**
* Method to initialize for a new text search.
* @param search the string to locate.
* @param caseSensitive true to match only where the case is the same.
* @param regExp true if the search string is a regular expression.
* @param whatToSearch a collection of text types to consider.
* @param codeRestr a restriction on types of Code to consider (null to consider all Code values).
* @param unitRestr a restriction on types of Units to consider (null to consider all Unit values).
* @param highlightedOnly true to search only in the highlighted area.
*/
public void initTextSearch(String search, boolean caseSensitive, boolean regExp,
Set<TextUtils.WhatToSearch> whatToSearch, CodeExpression.Code codeRestr, TextDescriptor.Unit unitRestr,
boolean highlightedOnly)
{
System.out.println("Text search not implemented for waveform windows");
}
/**
* Method to find the next occurrence of a string.
* @param reverse true to find in the reverse direction.
* @return true if something was found.
*/
public boolean findNextText(boolean reverse) { return false; }
/**
* Method to replace the text that was just selected with findNextText().
* @param replace the new text to replace.
*/
public void replaceText(String replace) {}
/**
* Method to replace all selected text.
* @param replace the new text to replace everywhere.
*/
public void replaceAllText(String replace) {}
/**
* Method to export directly PNG file.
* @param ep printable object.
* @param filePath
*/
public void writeImage(ElectricPrinter ep, String filePath)
{
BufferedImage img = getPrintImage(ep);
PNG.writeImage(img, filePath);
}
private int oldBackground, oldForeground;
private boolean changedColors = false;
/**
* Method to initialize for printing.
* @param ep the ElectricPrinter object.
* @param pageFormat information about the print job.
* @return true if no errors were found during initialization.
*/
public boolean initializePrinting(ElectricPrinter ep, PageFormat pageFormat)
{
oldForeground = User.getColor(User.ColorPrefType.WAVE_FOREGROUND);
oldBackground = User.getColor(User.ColorPrefType.WAVE_BACKGROUND);
User.setColor(User.ColorPrefType.WAVE_FOREGROUND, 0);
User.setColor(User.ColorPrefType.WAVE_BACKGROUND, 0xFFFFFF);
changedColors = true;
PrinterJob pj = ep.getPrintJob();
if (pj == null) return false; // error
ColorSupported cs = pj.getPrintService().getAttribute(ColorSupported.class);
if (cs == null) return false; // error
nowPrinting = 1;
if (cs.getValue() == 0) nowPrinting = 2;
Dimension oldSize = ep.getOldSize();
int pageWid = (int)pageFormat.getImageableWidth() * ep.getDesiredDPI() / 72;
int pageHei = (int)pageFormat.getImageableHeight() * ep.getDesiredDPI() / 72;
double scaleX = (double)pageWid / (double)oldSize.width;
double scaleY = (double)pageHei / (double)oldSize.height;
double scale = Math.min(scaleX, scaleY);
pageWid = (int)(oldSize.width * scale);
pageHei = (int)(oldSize.height * scale);
overall.setSize(pageWid, pageHei);
overall.validate();
redrawAllPanels();
overall.repaint();
return true;
}
/**
* Method to print window using offscreen canvas.
* @param ep printable object.
* @return the image to print (null on error).
*/
public BufferedImage getPrintImage(ElectricPrinter ep)
{
BufferedImage bImage = ep.getBufferedImage();
Dimension sz = getPanel().getSize();
if (bImage == null)
{
bImage = (BufferedImage)(overall.createImage(sz.width, sz.height));
ep.setBufferedImage(bImage);
}
Graphics2D g2d = (Graphics2D)ep.getGraphics();
if (g2d == null)
{
g2d = bImage.createGraphics();
}
// scale if there was an old image size
Dimension szOld = ep.getOldSize();
if (szOld != null)
{
double scaleX = (double)sz.width / (double)szOld.width;
double scaleY = (double)sz.height / (double)szOld.height;
double gSX = (double)szOld.width / (double)szOld.height;
double gSY = gSX * scaleY / scaleX;
g2d.translate(ep.getPageFormat().getImageableX(), ep.getPageFormat().getImageableY());
g2d.scale(72.0 / ep.getDesiredDPI() / gSX, 72.0 / ep.getDesiredDPI() / gSY);
}
overall.paint(g2d);
// if (mainHorizRulerPanel != null)
// mainHorizRulerPanel.paint(g2d);
if (changedColors)
{
User.setColor(User.ColorPrefType.WAVE_FOREGROUND, oldForeground);
User.setColor(User.ColorPrefType.WAVE_BACKGROUND, oldBackground);
changedColors = false;
}
nowPrinting = 0;
return bImage;
}
/**
* Method to pan along X or Y according to fixed amount of ticks
* @param direction 0 for horizontal, 1 for vertical.
* @param panningAmounts an array of distances, indexed by the current panning distance index.
* @param ticks the number of steps to take (usually 1 or -1).
*/
public void panXOrY(int direction, double[] panningAmounts, int ticks)
{
// determine the panel extent
double hRange = maxXPosition - minXPosition;
double vRange = -1;
double vRangeAny = -1;
for(Panel wp : wavePanels)
{
vRangeAny = wp.getYAxisRange();
if (wp.isSelected())
{
hRange = wp.getMaxXAxis() - wp.getMinXAxis();
vRange = wp.getYAxisRange();
break;
}
}
if (vRange < 0) vRange = vRangeAny;
double distance = ticks * panningAmounts[User.getPanningDistance()];
for(Panel wp : wavePanels)
{
if (direction == 0)
{
// pan horizontally
if (!xAxisLocked && !wp.isSelected()) continue;
double low = wp.getMinXAxis() - hRange * distance;
double high = wp.getMaxXAxis() - hRange * distance;
wp.setXAxisRange(low, high);
} else
{
// pan vertically
if (!wp.isSelected()) continue;
double low = wp.getYAxisLowValue() - vRange * distance;
double high = wp.getYAxisHighValue() - vRange * distance;
wp.setYAxisRange(low, high);
}
wp.repaintWithRulers();
}
}
/**
* Method to shift the panels so that the main cursor location becomes the center.
*/
public void centerCursor() {
double center = getMainXPositionCursor();
for(Iterator<Panel> it = getPanels(); it.hasNext(); ) {
Panel wp = it.next();
double half = (wp.getMaxXAxis() - wp.getMinXAxis())/2.;
wp.setXAxisRange(center-half, center+half);
wp.repaintWithRulers();
}
}
/**
* Method to set the window title.
*/
public void setWindowTitle()
{
if (wf == null) return;
String title = "";
if (sd.getEngine() != null) title = "Simulation of "; else title = "Waveforms of ";
wf.setTitle(wf.composeTitle(sd.getCell(), title, 0));
}
/**
* Method to return the top-level JPanel for this WaveformWindow.
* The actual WaveformWindow object is below the top level, surrounded by scroll bars and other display artifacts.
* @return the top-level JPanel for this WaveformWindow.
*/
public JPanel getPanel() { return overall; }
public void setCursor(Cursor cursor)
{
overall.setCursor(cursor);
table.setCursor(cursor);
for (JPanel p : wavePanels)
{
p.setCursor(cursor);
}
}
public void setCell(Cell cell, VarContext context, WindowFrame.DisplayAttributes displayAttributes)
{
sd.setCell(cell);
setWindowTitle();
}
/**
* Method to return the cell that is shown in this window.
* @return the cell that is shown in this window.
*/
public Cell getCell() { return sd.getCell(); }
/**
* Method to return the stimulus information associated with this WaveformWindow.
* @return the stimulus information associated with this WaveformWindow.
*/
public Stimuli getSimData() { return sd; }
public void bottomScrollChanged(int e) {}
public void rightScrollChanged(int e) {}
// ************************************* WINDOW CONTROL *************************************
/**
* Method to return the associated schematics or layout window for this WaveformWindow.
* @return the other window that is cross-linked to this.
* Returns null if none can be found.
*/
private WindowFrame findSchematicsWindow()
{
Cell cell = getCell();
if (cell == null) return null;
// look for the original cell to highlight it
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
if (wf.getContent().getCell() != cell) continue;
if (wf.getContent() instanceof EditWindow) return wf;
}
return null;
}
/**
* Method to return the waveform window associated with a Cell.
* There may be multiple such windows, and the most recently used is returned.
* @param cell the Cell whose waveform window is desired.
* @return the waveform window that is linked to a cell.
* Returns null if none can be found.
*/
public static WaveformWindow findWaveformWindow(Cell cell)
{
// look for the original cell to highlight it
WaveformWindow found = null;
int bestClock = 0;
for(Iterator<WindowFrame> it = WindowFrame.getWindows(); it.hasNext(); )
{
WindowFrame wf = it.next();
if (wf.getContent().getCell() != cell) continue;
if (wf.getContent() instanceof WaveformWindow)
{
WaveformWindow ww = (WaveformWindow)wf.getContent();
if (found == null || wf.getUsageClock() > bestClock)
{
found = ww;
bestClock = wf.getUsageClock();
}
}
}
return found;
}
/**
* Method to return the WindowFrame in which this WaveformWindow lives.
* @return the WindowFrame in which this WaveformWindow lives.
*/
public WindowFrame getWindowFrame() { return wf; }
public int getScreenLowX() { return screenLowX; }
public int getScreenHighX() { return screenHighX; }
public void setScreenXSize(int lowX, int highX) { screenLowX = lowX; screenHighX = highX; }
public JPanel getSignalNamesPanel() { return left; }
public JPanel getSignalTracesPanel() { return right; }
public JTable getWaveformTable() { return table; }
public Color getBackgroundColor() { return backgroundColor; }
// ************************************* CONTROL OF PANELS IN THE WINDOW *************************************
public int getNewPanelNumber()
{
int highestPanelNumber = 1;
for(Panel wp : wavePanels)
{
if (wp.getPanelNumber() >= highestPanelNumber)
highestPanelNumber = wp.getPanelNumber() + 1;
}
return highestPanelNumber;
}
/**
* Method to create a new panel with an X range similar to others on the display.
* @return the newly created Panel.
*/
public Panel makeNewPanel(int panelSize)
{
if (panelSize < 0)
{
if (sd.isAnalog()) panelSize = User.getWaveformAnalogPanelHeight(); else
panelSize = User.getWaveformDigitalPanelHeight();
}
// determine the X and Y ranges
double leftEdge, rightEdge;
leftEdge = sd.getMinTime();
rightEdge = sd.getMaxTime();
int vertAxisPos = -1;
if (xAxisLocked && wavePanels.size() > 0)
{
Panel aPanel = wavePanels.get(0);
leftEdge = aPanel.getMinXAxis();
rightEdge = aPanel.getMaxXAxis();
vertAxisPos = aPanel.getVertAxisPos();
}
int [] rowHeights = null;
int rows = table.getRowCount();
rowHeights = new int[rows+1];
for(int i=0; i<rows; i++) rowHeights[i] = table.getRowHeight(i);
rowHeights[rows] = panelSize;
// create the new panel
Panel panel = new Panel(this, panelSize);
// set the X and Y ranges
panel.setXAxisRange(leftEdge, rightEdge);
if (vertAxisPos > 0) panel.setVertAxisPos(vertAxisPos);
// show and return the panel
panel.makeSelectedPanel(-1, -1);
getPanel().validate();
if (getMainHorizRuler() != null)
getMainHorizRuler().repaint();
table.tableChanged(new TableModelEvent(tableModel));
for(int i=0; i<rowHeights.length; i++) table.setRowHeight(i, rowHeights[i]);
table.setRowHeight(rowHeights[0]);
return panel;
}
/**
* Method to return the number of Panels in this WaveformWindow.
* @return the number of Panels in this WaveformWindow.
*/
public int getNumPanels() { return wavePanels.size(); }
/**
* Method to return a Panel in this window.
* @param index the panel number to get.
* @return a Panel in this window.
*/
public Panel getPanel(int index) { return wavePanels.get(index); }
/**
* Method to return the index of a Panel in this window.
* @param panel the Panel to find.
* @return the index of that Panel in this window.
*/
public int getPanelIndex(Panel panel) { return wavePanels.indexOf(panel); }
/**
* Method to add a Panel to this window.
*/
public void addPanel(Panel panel)
{
wavePanels.add(panel);
}
/**
* Method to add a Panel to this window.
*/
public void addPanel(Panel panel, int index)
{
wavePanels.add(index, panel);
}
/**
* Method to remove a Panel from this window.
*/
public void removePanel(Panel panel)
{
wavePanels.remove(panel);
}
/**
* Method to return an Iterator over the Panel in this window.
* @return an Iterator over the Panel in this window.
*/
public Iterator<Panel> getPanels() { return wavePanels.iterator(); }
/**
* Method to return the current printing mode.
* @return 0: color display (default), 1: color printing, 2: B&W printing
*/
public int getPrintingMode() { return nowPrinting; }
/**
* Method to return a Panel, given its number.
* @param panelNumber the number of the desired Panel.
* @return the Panel with that number (null if not found).
*/
private Panel getPanelFromNumber(int panelNumber)
{
for(Panel wp : wavePanels)
{
if (wp.getPanelNumber() == panelNumber) return wp;
}
return null;
}
private void togglePanelName()
{
if (rebuildingSignalNameList) return;
String panelName = (String)signalNameList.getSelectedItem();
int spacePos = panelName.indexOf(' ');
if (spacePos >= 0) panelName = panelName.substring(spacePos+1);
int index = TextUtils.atoi(panelName);
// toggle its state
for(Panel wp : wavePanels)
{
if (wp.getPanelNumber() == index)
{
if (wp.isHidden())
{
showPanel(wp);
} else
{
hidePanel(wp);
}
break;
}
}
}
public void validatePanel() { overall.validate(); }
public void rebuildPanelList()
{
rebuildingSignalNameList = true;
signalNameList.removeAllItems();
boolean hasSignals = false;
for(Panel wp : wavePanels)
{
signalNameList.addItem("Panel " + Integer.toString(wp.getPanelNumber()) + (wp.isHidden() ? " (HIDDEN)" : ""));
hasSignals = true;
}
if (hasSignals) signalNameList.setSelectedIndex(0);
rebuildingSignalNameList = false;
}
public void redrawAllPanels()
{
if (mainHorizRulerPanel != null)
mainHorizRulerPanel.repaint();
for(Panel wp : wavePanels)
{
wp.repaintContents();
}
table.repaint();
}
public void repaintAllPanels()
{
table.repaint();
}
/**
* Method called when a Panel is to be closed.
* @param wp the Panel to close.
*/
public void closePanel(Panel wp)
{
int rows = wavePanels.size();
int [] rowHeights = new int[rows];
int closedPanelIndex = wavePanels.indexOf(wp);
int validPanels = 0;
int visRow = 0;
for(int i=0; i<rows; i++)
{
if (wavePanels.get(i).isHidden()) continue;
int rowHeight = table.getRowHeight(visRow++);
if (i == closedPanelIndex) continue;
rowHeights[validPanels++] = rowHeight;
}
stopEditing();
reloadTable();
wavePanels.remove(wp);
for(int i=0; i<validPanels; i++) table.setRowHeight(i, rowHeights[i]);
rebuildPanelList();
overall.validate();
redrawAllPanels();
}
/**
* Method called when a Panel is to be hidden.
* @param wp the Panel to hide.
*/
public void hidePanel(Panel wp)
{
if (wp.isHidden()) return;
int rows = wavePanels.size();
int [] rowHeights = new int[rows];
int hiddenPanelIndex = wavePanels.indexOf(wp);
int validPanels = 0;
int visRow = 0;
for(int i=0; i<rows; i++)
{
if (wavePanels.get(i).isHidden()) continue;
int rowHeight = table.getRowHeight(visRow++);
if (i == hiddenPanelIndex) continue;
rowHeights[validPanels++] = rowHeight;
}
wp.setHidden(true);
stopEditing();
reloadTable();
for(int i=0; i<validPanels; i++) table.setRowHeight(i, rowHeights[i]);
rebuildPanelList();
overall.validate();
redrawAllPanels();
}
/**
* Method called when a Panel is to be shown.
* @param wp the Panel to show.
*/
public void showPanel(Panel wp)
{
if (!wp.isHidden()) return;
int rows = wavePanels.size();
int [] rowHeights = new int[rows];
int openedPanelIndex = wavePanels.indexOf(wp);
int validPanels = 0;
int visRow = 0;
int openedPanelHeightIndex = -1;
int averageHeight = 0;
int numAverages = 0;
for(int i=0; i<rows; i++)
{
if (i == openedPanelIndex)
{
openedPanelHeightIndex = validPanels;
int height = User.getWaveformDigitalPanelHeight();
if (sd.isAnalog()) height = User.getWaveformAnalogPanelHeight();
rowHeights[validPanels++] = height;
continue;
}
if (wavePanels.get(i).isHidden()) continue;
int rowHeight = table.getRowHeight(visRow++);
rowHeights[validPanels++] = rowHeight;
averageHeight += rowHeight;
numAverages++;
}
wp.setHidden(false);
stopEditing();
reloadTable();
if (numAverages != 0)
rowHeights[openedPanelHeightIndex] = averageHeight / numAverages;
for(int i=0; i<validPanels; i++) table.setRowHeight(i, rowHeights[i]);
rebuildPanelList();
overall.validate();
redrawAllPanels();
}
/**
* Method called to grow or shrink the panels vertically.
*/
public void growPanels(double scale)
{
// adjust the default analog panel size
int origPanelSize = User.getWaveformAnalogPanelHeight();
int newPanelSize = (int)(origPanelSize * scale);
if (newPanelSize < MINPANELHEIGHT) newPanelSize = MINPANELHEIGHT;
if (origPanelSize != newPanelSize) User.setWaveformAnalogPanelHeight(newPanelSize);
// adjust the default digital panel size
origPanelSize = User.getWaveformDigitalPanelHeight();
newPanelSize = (int)(origPanelSize * scale);
if (newPanelSize < MINPANELHEIGHT) newPanelSize = MINPANELHEIGHT;
if (origPanelSize != newPanelSize) User.setWaveformDigitalPanelHeight(newPanelSize);
// resize the panels
for(int i=0; i<table.getRowCount(); i++)
{
int rowHeight = table.getRowHeight(i);
int newRowHeight = (int)(rowHeight*scale);
Panel wp = wavePanels.get(i);
if (wp.isAnalog())
{
if (newRowHeight < MINPANELHEIGHT) newRowHeight = MINPANELHEIGHT;
} else
{
if (newRowHeight < MINPANELHEIGHT) newRowHeight = MINPANELHEIGHT;
}
table.setRowHeight(i, newRowHeight);
wp.updatePanelTitle();
}
overall.validate();
redrawAllPanels();
}
/**
* Method called to delete the highlighted signal from its Panel.
* @param wp the Panel with the signal to be deleted.
*/
public void deleteSignalFromPanel(Panel wp)
{
boolean found = true;
while (found)
{
found = false;
for(WaveSignal ws : wp.getSignals())
{
if (!ws.isHighlighted()) continue;
wp.removeHighlightedSignal(ws, true);
wp.removeSignal(ws.getButton());
found = true;
break;
}
}
if (wp.getSignalButtons() != null)
{
wp.getSignalButtons().validate();
wp.getSignalButtons().repaint();
}
wp.repaintContents();
saveSignalOrder();
}
/**
* Method called to delete all signals from a Panel.
* @param wp the Panel to clear.
*/
public void deleteAllSignalsFromPanel(Panel wp)
{
wp.clearHighlightedSignals();
wp.getSignalButtons().removeAll();
wp.getSignalButtons().validate();
wp.getSignalButtons().repaint();
wp.removeAllSignals();
wp.repaintContents();
saveSignalOrder();
}
// ************************************* THE HORIZONTAL RULER *************************************
public HorizRuler getMainHorizRuler() { return mainHorizRulerPanel; }
public Signal<?> getXAxisSignalAll() { return xAxisSignalAll; }
public void setXAxisSignalAll(Signal<?> sig) { xAxisSignalAll = sig; }
private void addMainHorizRulerPanel()
{
mainHorizRulerPanel = new HorizRuler(null, this);
mainHorizRulerPanel.setToolTipText("One X axis ruler applies to all signals when the X axes are locked");
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 10; gbc.gridy = 1;
gbc.weightx = 1; gbc.weighty = 0;
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
overall.add(mainHorizRulerPanel, gbc);
}
private void removeMainHorizRulerPanel()
{
overall.remove(mainHorizRulerPanel);
mainHorizRulerPanel = null;
}
// ************************************* SWEEP CONTROL *************************************
private void resetSweeps()
{
sweepSignals = new HashMap<String,SweepSignal[]>();
for(Iterator<SignalCollection> it = sd.getSignalCollections(); it.hasNext(); )
{
SignalCollection sc = it.next();
String scName = sc.getName();
SweepSignal[] signalArray = sweepSignals.get(scName);
if (signalArray == null)
{
String[] sweeps = sc.getSweepNames();
if (sweeps == null) continue;
signalArray = new SweepSignal[sweeps.length];
for(int i=0; i<sweeps.length; i++)
signalArray[i] = new SweepSignal(sweeps[i], this);
sweepSignals.put(scName, signalArray);
}
}
}
public void setIncludeInAllSweeps(List<SweepSignal> sweeps, boolean include)
{
for(int i=0; i<sweeps.size(); i++)
{
SweepSignal ss = sweeps.get(i);
boolean update = (i == sweeps.size()-1);
ss.setIncluded(include, update);
}
}
/**
* Method to check whether this particular sweep is included.
* @return true if the sweep is included
*/
public boolean isSweepSignalIncluded(String scName, int index)
{
SweepSignal[] signalArray = sweepSignals.get(scName);
if (signalArray != null)
{
SweepSignal ss = signalArray[index];
if (ss != null) return ss.isIncluded();
}
return true; // in case no sweep, always true
}
public int getHighlightedSweep() { return highlightedSweep; }
public void setHighlightedSweep(int sweep) { highlightedSweep = sweep; }
// ************************************* VCR CONTROL *************************************
private void tick()
{
// see if it is time to advance the VCR
long curtime = System.currentTimeMillis();
if (curtime - vcrLastAdvance < 100) return;
vcrLastAdvance = curtime;
if (wavePanels.size() == 0) return;
Panel wp = wavePanels.iterator().next();
int xValueScreen = wp.convertXDataToScreen(mainXPosition);
if (vcrPlayingBackwards)
{
int newXValueScreen = xValueScreen - vcrAdvanceSpeed;
double newXValue = wp.convertXScreenToData(newXValueScreen);
double lowXValue = sd.getMinTime();
if (newXValue <= lowXValue)
{
newXValue = lowXValue;
vcrClickStop();
}
setMainXPositionCursor(newXValue);
} else
{
int newXValueScreen = xValueScreen + vcrAdvanceSpeed;
double newXValue = wp.convertXScreenToData(newXValueScreen);
double highXValue = sd.getMaxTime();
if (newXValue >= highXValue)
{
newXValue = highXValue;
vcrClickStop();
}
setMainXPositionCursor(newXValue);
}
redrawAllPanels();
}
public void vcrClickRewind()
{
vcrClickStop();
double lowXValue = sd.getMinTime();
setMainXPositionCursor(lowXValue);
redrawAllPanels();
}
public void vcrClickPlayBackwards()
{
if (vcrTimer == null)
{
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt) { tick(); }
};
vcrTimer = new Timer(100, taskPerformer);
vcrLastAdvance = System.currentTimeMillis();
vcrTimer.start();
}
vcrPlayingBackwards = true;
}
/**
* Method to stop the auto-playing in the simulation window.
*/
public void vcrClickStop()
{
if (vcrTimer == null) return;
vcrTimer.stop();
vcrTimer = null;
}
public void vcrClickPlay()
{
if (vcrTimer == null)
{
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt) { tick(); }
};
vcrTimer = new Timer(100, taskPerformer);
vcrLastAdvance = System.currentTimeMillis();
vcrTimer.start();
}
vcrPlayingBackwards = false;
}
public void vcrClickToEnd()
{
vcrClickStop();
double highXValue = sd.getMaxTime();
setMainXPositionCursor(highXValue);
redrawAllPanels();
}
public void vcrClickFaster()
{
int j = vcrAdvanceSpeed / 4;
if (j <= 0) j = 1;
vcrAdvanceSpeed += j;
}
public void vcrClickSlower()
{
int j = vcrAdvanceSpeed / 4;
if (j <= 0) j = 1;
vcrAdvanceSpeed -= j;
if (vcrAdvanceSpeed <= 0) vcrAdvanceSpeed = 1;
}
// ************************************* HIGHLIGHTING *************************************
/**
* Method to remove all highlighting from waveform window.
*/
public void clearHighlighting()
{
// look at all signal names in the cell
for(Panel wp : wavePanels)
{
// look at all traces in this panel
boolean changed = false;
for(WaveSignal ws : wp.getSignals())
{
if (ws.isHighlighted()) changed = true;
ws.setHighlighted(false);
}
if (changed) wp.repaintContents();
}
}
/**
* Method to return a List of highlighted simulation signals.
* @return a List of highlighted simulation signals.
*/
public List<Signal<?>> getHighlightedNetworkNames()
{
List<Signal<?>> highlightedSignals = new ArrayList<Signal<?>>();
// look at all signal names in the cell
for(Panel wp : wavePanels)
{
// look at all traces in this panel
for(WaveSignal ws : wp.getSignals())
{
if (ws.isHighlighted()) highlightedSignals.add(ws.getSignal());
}
}
// also include what is in the SIGNALS tree
ExplorerTree sigTree = wf.getExplorerTab();
Object nodeInfo = sigTree.getCurrentlySelectedObject(0);
if (nodeInfo != null && nodeInfo instanceof Signal<?>)
{
Signal<?> sig = (Signal<?>)nodeInfo;
highlightedSignals.add(sig);
}
return highlightedSignals;
}
/**
* Method to get a Set of currently highlighted networks in this WaveformWindow.
*/
public Set<Network> getHighlightedNetworks()
{
// make empty set
Set<Network> nets = new HashSet<Network>();
// if no cell in the window, stop now
Cell cell = sd.getCell();
if (cell == null) return nets;
Netlist netlist = cell.getNetlist();
if (netlist == null)
{
System.out.println("Sorry, a deadlock aborted crossprobing (network information unavailable). Please try again");
return nets;
}
// look at all signal names in the cell
for(Panel wp : wavePanels)
{
// look at all traces in this panel
for(WaveSignal ws : wp.getSignals())
{
Network net = findNetwork(netlist, ws.getSignal().getSignalName());
if (net != null) nets.add(net);
}
}
// also include what is in the SIGNALS tree
ExplorerTree sigTree = wf.getExplorerTab();
Object nodeInfo = sigTree.getCurrentlySelectedObject(0);
if (nodeInfo != null && nodeInfo instanceof Signal<?>)
{
Signal<?> sig = (Signal<?>)nodeInfo;
Network net = findNetwork(netlist, sig.getSignalName());
if (net != null) nets.add(net);
}
return nets;
}
/**
* Get the highlighter for this window content.
* @return the highlighter
*/
public Highlighter getHighlighter() { return highlighter; }
// ************************************* PRINTING *************************************
/**
* Method to get a list of polygons describing the waveform window.
* @return a List of PolyBase objects that describe this window.
*/
public List<PolyBase> getPolysForPrinting()
{
int offY = 0;
List<PolyBase> override = new ArrayList<PolyBase>();
HorizRuler mainHR = getMainHorizRuler();
if (mainHR != null)
{
List<PolyBase> horizPolys = mainHR.getPolysForPrinting(getPanels().next());
for(PolyBase poly : horizPolys)
{
Point2D [] pts = poly.getPoints();
for(int i=0; i<pts.length; i++)
{
poly.setPoint(i, pts[i].getX(), pts[i].getY() + offY);
}
override.add(poly);
}
offY += mainHR.getHeight();
}
for(Iterator<Panel> it = getPanels(); it.hasNext(); )
{
Panel panel = it.next();
HorizRuler hr = panel.getHorizRuler();
if (hr != null)
{
offY += hr.getHeight();
List<PolyBase> horizPolys = hr.getPolysForPrinting(panel);
for(PolyBase poly : horizPolys)
{
Point2D [] pts = poly.getPoints();
for(int i=0; i<pts.length; i++)
{
poly.setPoint(i, pts[i].getX(), pts[i].getY() + offY);
}
override.add(poly);
}
offY += hr.getHeight();
}
List<PolyBase> panelList = panel.getPolysForPrinting();
for(PolyBase poly : panelList)
{
Point2D [] pts = poly.getPoints();
for(int i=0; i<pts.length; i++)
{
poly.setPoint(i, pts[i].getX(), pts[i].getY() + offY);
}
override.add(poly);
}
offY += panel.getHeight();
if (hr == null) offY += 20;
}
return override;
}
// ************************************* THE EXPLORER TREE *************************************
public List<MutableTreeNode> loadExplorerTrees()
{
TreePath rootPath = new TreePath(ExplorerTreeModel.rootNode);
ArrayList<MutableTreeNode> nodes = new ArrayList<MutableTreeNode>();
treePathFromSignal.clear();
for(Iterator<SignalCollection> it = sd.getSignalCollections(); it.hasNext(); )
{
SignalCollection sc = it.next();
nodes.add(getSignalsForExplorer(sc, rootPath, sc.getName()));
DefaultMutableTreeNode sweepTree = getSweepsForExplorer(sc, sc.getName());
if (sweepTree != null) nodes.add(sweepTree);
}
// clean possible nulls
while (nodes.remove(null));
return nodes;
}
public void loadTechnologies() {
}
private DefaultMutableTreeNode getSignalsForExplorer(SignalCollection sc, TreePath parentPath, String collectionName)
{
Iterable<Signal<?>> signalsi = sc.getSignals();
ArrayList<Signal<?>> signals = new ArrayList<Signal<?>>();
// find bussed signals
Set<Signal<?>> busMembers = new HashSet<Signal<?>>();
for(Signal<?> s : signalsi)
{
Signal<?>[] members = s.getBusMembers();
if (members == null) continue;
for(int i=0; i<members.length; i++) busMembers.add(members[i]);
}
for(Signal<?> s : signalsi)
if (!busMembers.contains(s)) signals.add(s);
if (signals.size()==0) return null;
DefaultMutableTreeNode signalsExplorerTree = new DefaultMutableTreeNode(collectionName);
TreePath collectionPath = parentPath.pathByAddingChild(signalsExplorerTree);
for (Signal<?> s : sc.getSignals())
treePathFromSignal.put(s, collectionPath);
Map<String,TreePath> contextMap = new HashMap<String,TreePath>();
contextMap.put("", collectionPath);
Collections.sort(signals, new SignalsByName());
// add branches first
char separatorChar = sd.getSeparatorChar();
for(Signal<?> sSig : signals)
{
if (sSig.getSignalContext() != null)
makeContext(sSig.getSignalContext(), contextMap, separatorChar);
}
String delim = sd.getNetDelimiter(); //SimulationTool.getSpiceExtractedNetDelimiter();
// make a list of signal names with "#" in them
Set<String> sharpSet = new HashSet<String>();
for(Signal<?> sSig : signals)
{
String sigName = sSig.getSignalName();
int hashPos = sigName.indexOf(delim);
if (hashPos > 0)
{
String nodeName = sSig.getSignalContext();
if (nodeName == null) nodeName = ""; else nodeName += separatorChar;
nodeName += sigName.substring(0, hashPos);
sharpSet.add(nodeName);
}
}
// add all signals to the tree
for(Signal<?> sSig : signals)
{
TreePath thisTree = collectionPath;
String nodeName = sSig.getSignalContext();
String nodeNameStr = nodeName;
if (nodeNameStr == null) nodeNameStr = ""; else nodeNameStr += separatorChar;
String sigName = sSig.getSignalName();
int hashPos = sigName.indexOf(delim);
if (hashPos > 0)
{
// force a branch with the proper name
nodeName = nodeNameStr + sigName.substring(0, hashPos+1);
} else
{
// if this is the pure name of a hash set, force a branch
String pureSharpName = nodeNameStr + sigName;
if (sharpSet.contains(pureSharpName))
nodeName = pureSharpName + delim;
}
if (nodeName != null)
thisTree = makeContext(nodeName, contextMap, separatorChar);
DefaultMutableTreeNode sigLeaf = new DefaultMutableTreeNode(sSig);
((DefaultMutableTreeNode)thisTree.getLastPathComponent()).add(sigLeaf);
}
return signalsExplorerTree;
}
/**
* Recursive method to locate and create branches in the Signal Explorer tree.
* @param branchName the name of a branch to find/create.
* The name has dots in it to separate levels of the hierarchy.
* @param contextMap a HashMap of branch names to tree paths.
* @return the tree path for the requested branch name.
*/
private TreePath makeContext(String branchName, Map<String,TreePath> contextMap, char separatorChar)
{
TreePath branchTree = contextMap.get(branchName);
if (branchTree != null) return branchTree;
// split the branch name into a leaf and parent
String parent = "";
String leaf = branchName;
int dotPos = leaf.lastIndexOf(separatorChar);
if (dotPos >= 0)
{
parent = leaf.substring(0, dotPos);
leaf = leaf.substring(dotPos+1);
}
TreePath parentBranch = makeContext(parent, contextMap, separatorChar);
DefaultMutableTreeNode thisTree = new DefaultMutableTreeNode(leaf);
((DefaultMutableTreeNode)parentBranch.getLastPathComponent()).add(thisTree);
TreePath thisPath = parentBranch.pathByAddingChild(thisTree);
contextMap.put(branchName, thisPath);
return thisPath;
}
/**
* Class to sort signals by their name
*/
public static class SignalsByName implements Comparator<Signal<?>>
{
public int compare(Signal<?> s1, Signal<?> s2)
{
return TextUtils.STRING_NUMBER_ORDER.compare(s1.getFullName(), s2.getFullName());
}
}
private DefaultMutableTreeNode getSweepsForExplorer(SignalCollection sc, String collectionName)
{
DefaultMutableTreeNode sweepsExplorerTree = null;
boolean first = true;
SweepSignal[] signalArray = sweepSignals.get(sc.getName());
if (signalArray != null)
{
for(SweepSignal ss : signalArray)
{
if (first)
{
first = false;
int spacePos = collectionName.indexOf(' ');
if (spacePos >= 0) collectionName = collectionName.substring(0, spacePos) + " SWEEPS"; else
collectionName += " SWEEPS";
sweepsExplorerTree = new DefaultMutableTreeNode(collectionName);
}
sweepsExplorerTree.add(new DefaultMutableTreeNode(ss));
}
}
return sweepsExplorerTree;
}
// ************************************* SIGNALS *************************************
private Signal<?> findSignal(String name, SignalCollection sc)
{
for(Signal<?> sSig : sc.getSignals())
{
String sigName = sSig.getFullName();
if (sigName.equals(name)) return sSig;
}
return null;
}
/**
* Method to add a selection to the waveform display.
* @param h a Highlighter of what is selected.
* @param context the context of these networks
* (a string to prepend to them to get the actual simulation signal name).
* @param newPanel true to create new panels for each signal.
*/
public void showSignals(Highlighter h, VarContext context, boolean newPanel)
{
List<Signal<?>> found = findSelectedSignals(h, context);
showSignals(found, newPanel);
}
/**
* Method to add a list of signals to the waveform display.
* @param found the signals to add.
* @param newPanel true to create new panels for each signal.
*/
public void showSignals(List<Signal<?>> found, boolean newPanel)
{
// determine the current panel
Panel wp = null;
for(Panel oWp : wavePanels)
{
if (oWp.isSelected())
{
wp = oWp;
break;
}
}
if (!newPanel && wp == null)
{
System.out.println("No current waveform panel to add signals");
return;
}
boolean added = false;
for(Signal<?> sSig : found) {
// add the signal
if (newPanel) {
wp = makeNewPanel(-1);
wp.fitToSignal(sSig);
newPanel = false;
if (!xAxisLocked)
wp.setXAxisRange(sSig.getMinTime(), sSig.getMaxTime());
}
// check if signal already in panel
boolean alreadyPlotted = false;
for(WaveSignal ws : wp.getSignals())
{
String name = ws.getSignal().getFullName();
if (name.equals(sSig.getFullName())) {
alreadyPlotted = true;
// add it again, this will increment colors
WaveSignal.addSignalToPanel(ws.getSignal(), wp, null);
}
}
if (!alreadyPlotted) {
new WaveSignal(wp, sSig);
}
added = true;
wp.repaintContents();
}
if (added)
{
overall.validate();
saveSignalOrder();
}
}
/**
* Method to remove a set of Networks from the waveform display.
* @param nets the Set of Networks to remove.
* @param context the context of these networks
* (a string to prepend to them to get the actual simulation signal name).
*/
public void removeSignals(Set<Network> nets, VarContext context)
{
for(Network net : nets)
{
String netName = getSpiceNetName(context, net);
for(Iterator<SignalCollection> aIt = sd.getSignalCollections(); aIt.hasNext(); )
{
SignalCollection sc = aIt.next();
Signal<?> sSig = findSignalForNetwork(sc, netName);
if (sSig == null) continue;
boolean found = true;
while (found)
{
found = false;
for(Iterator<Panel> pIt = getPanels(); pIt.hasNext(); )
{
Panel wp = pIt.next();
for(WaveSignal ws : wp.getSignals())
{
if (ws.getSignal() != sSig) continue;
wp.removeHighlightedSignal(ws, true);
wp.removeSignal(ws.getButton());
wp.getSignalButtons().validate();
wp.getSignalButtons().repaint();
wp.repaintContents();
found = true;
break;
}
if (found) break;
}
}
}
}
}
public static String getSpiceNetName(Network net)
{
return Spice.getSafeNetName(net.getName(), SimulationTool.getSpiceEngine());
}
/**
* Get the spice net name associated with the network and the context.
* If the network is null, a String describing only the context is returned.
* @param context the context
* @param net the network, or null
* @return a String describing the unique, global spice name for the network,
* or a String describing the context if net is null
*/
public static String getSpiceNetName(VarContext context, Network net)
{
return getSpiceNetName(context, net, false, false);
}
/**
* Get the spice net name associated with the network and the context.
* If the network is null, a String describing only the context is returned.
* @param context the context
* @param net the network, or null
* @param assuraRCXFormat return net assuming Assura RCX flat netlist format
* @param starRCXTFormat return net assuming Star RCXT flat netlist format
* @return a String describing the unique, global spice name for the network,
* or a String describing the context if net is null
*/
public static String getSpiceNetName(VarContext context, Network net, boolean assuraRCXFormat, boolean starRCXTFormat)
{
boolean isGlobal = false;
if (net != null)
{
Netlist netlist = net.getNetlist();
while (net.isExported() && (context != VarContext.globalContext))
{
// net is exported, find net in parent
Network tempnet = getNetworkInParent(net, context.getNodable());
if (tempnet == null)
break;
net = tempnet;
context = context.pop();
}
// searching in globals
// Code taken from NCC
netlist = net.getNetlist();
Global.Set globNets = netlist.getGlobals();
for (int i=0; i<globNets.size(); i++)
{
Global g = globNets.get(i);
Network netG = netlist.getNetwork(g);
if (netG == net)
{
isGlobal = true;
break;
}
}
}
// create net name
String contextStr = context.getInstPath(".");
if (assuraRCXFormat) {
contextStr = "x"+context.getInstPath("/x");
}
if (starRCXTFormat) {
contextStr = context.getInstPath("/");
}
contextStr = TextUtils.canonicString(contextStr);
if (net == null) return contextStr;
if (context == VarContext.globalContext || isGlobal)
return getSpiceNetName(net);
else if (assuraRCXFormat) return contextStr + "/" + getSpiceNetName(net);
else if (starRCXTFormat) return contextStr + "/" + getSpiceNetName(net);
else return contextStr + "." + getSpiceNetName(net);
}
/**
* Get the Network in the childNodable's parent that corresponds to the Network
* inside the childNodable.
* @param childNetwork the network in the childNodable
* @return the network in the parent that connects to the
* specified network, or null if no such network.
* null on error.
*/
public static Network getNetworkInParent(Network childNetwork, Nodable childNodable) {
if (childNodable == null || childNetwork == null) return null;
if (!childNodable.isCellInstance()) return null;
Cell childCell = (Cell)childNodable.getProto();
if (childCell.contentsView() != null)
childCell = childCell.contentsView();
// find export on network
boolean found = false;
Export export = null;
int i = 0;
for (Iterator<PortProto> it = childCell.getPorts(); it.hasNext(); )
{
export = (Export)it.next();
for (i=0; i<export.getNameKey().busWidth(); i++) {
Netlist netlist = childCell.getNetlist();
if (netlist == null)
{
System.out.println("Sorry, a deadlock aborted crossprobing (network information unavailable). Please try again");
return null;
}
Network net = netlist.getNetwork(export, i);
if (net == childNetwork) { found = true; break; }
}
if (found) break;
}
if (!found) return null;
// find corresponding port on icon
Export pp = (Export)childNodable.getProto().findPortProto(export.getNameKey());
// find corresponding network in parent
Cell parentCell = childNodable.getParent();
//if (childNodable instanceof NodeInst) childNodable = Netlist.getNodableFor((NodeInst)childNodable, 0);
Netlist netlist = parentCell.getNetlist();
if (netlist == null)
{
System.out.println("Sorry, a deadlock aborted crossprobing (network information unavailable). Please try again");
return null;
}
Network parentNet = netlist.getNetwork(childNodable, pp, i);
return parentNet;
}
/**
* Method to locate a simulation signal in the waveform.
* @param sSig the Signal to locate.
* @return the displayed WaveSignal where it is in the waveform window.
* Returns null if the signal is not being displayed.
*/
public WaveSignal findDisplayedSignal(Signal<?> sSig)
{
for(Panel wp : wavePanels)
{
WaveSignal ws = wp.findWaveSignal(sSig);
if (ws != null) return ws;
}
return null;
}
// ************************************* THE X AXIS *************************************
public double getMainXPositionCursor() { return mainXPosition; }
public void setMainXPositionCursor(double value)
{
mainXPosition = value;
String amount = TextUtils.convertToEngineeringNotation(mainXPosition, "s");
mainPos.setText("Main: " + amount);
String diff = TextUtils.convertToEngineeringNotation(Math.abs(mainXPosition - extXPosition), "s");
delta.setText("Delta: " + diff);
updateAssociatedLayoutWindow();
}
public double getExtensionXPositionCursor() { return extXPosition; }
public void setExtensionXPositionCursor(double value)
{
extXPosition = value;
String amount = TextUtils.convertToEngineeringNotation(extXPosition, "s");
extPos.setText("Ext: " + amount);
String diff = TextUtils.convertToEngineeringNotation(Math.abs(mainXPosition - extXPosition), "s");
delta.setText("Delta: " + diff);
}
/**
* Method to set the X range in all panels.
* @param minXPosition the low X value.
* @param maxXPosition the high X value.
*/
public void setDefaultHorizontalRange(double minXPosition, double maxXPosition)
{
this.minXPosition = minXPosition;
this.maxXPosition = maxXPosition;
}
public double getLowDefaultHorizontalRange() { return minXPosition; }
public double getHighDefaultHorizontalRange() { return maxXPosition; }
/**
* Method to set the zoom extents for this waveform window.
* @param lowVert the low value of the vertical axis (for the given panel only).
* @param highVert the high value of the vertical axis (for the given panel only).
* @param lowHoriz the low value of the horizontal axis (for the given panel only unless X axes are locked).
* @param highHoriz the high value of the horizontal axis (for the given panel only unless X axes are locked).
* @param thePanel the panel being zoomed.
*/
public void setZoomExtents(double lowVert, double highVert, double lowHoriz, double highHoriz, Panel thePanel)
{
for(Panel wp : wavePanels)
{
boolean changed = false;
if (wp == thePanel)
{
wp.setYAxisRange(lowVert, highVert);
changed = true;
}
if (xAxisLocked || wp == thePanel)
{
if (wp.getMinXAxis() < wp.getMaxXAxis())
{
wp.setXAxisRange(Math.min(lowHoriz, highHoriz), Math.max(lowHoriz, highHoriz));
} else
{
wp.setXAxisRange(Math.max(lowHoriz, highHoriz), Math.min(lowHoriz, highHoriz));
}
changed = true;
}
if (changed) wp.repaintWithRulers();
}
}
/**
* Method called to toggle the lock on the horizontal axes.
*/
public void togglePanelXAxisLock()
{
xAxisLocked = ! xAxisLocked;
if (xAxisLocked)
{
// X axes now locked: add main ruler, remove individual rulers
xAxisLockButton.setIcon(iconLockXAxes);
addMainHorizRulerPanel();
double minXPosition = 0, maxXPosition = 0;
int vertAxis = 0;
boolean first = true;
for(Panel wp : wavePanels)
{
wp.removeHorizRulerPanel();
if (first)
{
first = false;
minXPosition = wp.getMinXAxis();
maxXPosition = wp.getMaxXAxis();
vertAxis = wp.getVertAxisPos();
} else
{
if (minXPosition < maxXPosition)
{
minXPosition = Math.min(minXPosition, wp.getMinXAxis());
maxXPosition = Math.max(maxXPosition, wp.getMaxXAxis());
} else
{
minXPosition = Math.max(minXPosition, wp.getMinXAxis());
maxXPosition = Math.min(maxXPosition, wp.getMaxXAxis());
}
wp.setVertAxisPos(vertAxis);
}
}
// force all panels to be at the same X position
for(Panel wp : wavePanels)
{
wp.setXAxisRange(minXPosition, maxXPosition);
}
} else
{
// X axes are unlocked: put a ruler in each panel, remove main ruler
xAxisLockButton.setIcon(iconUnLockXAxes);
for(Panel wp : wavePanels)
{
wp.addHorizRulerPanel();
}
removeMainHorizRulerPanel();
}
overall.validate();
overall.repaint();
}
public boolean isXAxisLocked() { return xAxisLocked; }
// ************************************* CROSS-PROBING *************************************
/**
* Method to crossprobe from an EditWindow to this WaveformWindow.
* @param wnd the EditWindow that changed.
* @param which the Highlighter in that window with current selection.
*/
private void crossProbeEditWindowToWaveform(EditWindow wnd, Highlighter which)
{
// make sure the windows are associated with each other
Locator loc = new Locator(wnd, this);
if (loc.getWaveformWindow() != this) return;
freezeEditWindowHighlighting = true;
// start by removing all highlighting in the waveform
for(Panel wp : wavePanels)
{
wp.clearHighlightedSignals();
}
// also clear "Signals" tree highlighting
ExplorerTree tree = wf.getExplorerTab();
tree.setSelectionPath(null);
tree.clearCurrentlySelectedObjects();
// find the signal to show in the waveform window
List<Signal<?>> found = findSelectedSignals(which, loc.getContext());
// show it in every panel
boolean foundSignal = false;
for(Panel wp : wavePanels)
{
for(WaveSignal ws : wp.getSignals())
{
for(Signal<?> sSig : found)
{
if (ws.getSignal() == sSig)
{
wp.addHighlightedSignal(ws, false);
foundSignal = true;
}
}
}
}
if (foundSignal) repaint();
// show only one in the "Signals" tree
Collections.sort(found, new SignalsByName());
for(Signal<?> sSig : found)
{
TreePath treePath = treePathFromSignal(sSig);
if (treePath != null) {
tree.setSelectionPath(treePath);
break;
}
}
freezeEditWindowHighlighting = false;
}
private TreePath treePathFromSignal(Signal<?> sig) {
TreePath treePath = treePathFromSignal.get(sig);
if (treePath == null) return null;
String fullName = sig.getFullName();
char separator = sd.getSeparatorChar();
int sBeg = 0;
while (sBeg < fullName.length()) {
int sEnd = fullName.indexOf(separator, sBeg);
if (sEnd < 0) sEnd = fullName.length();
String s = fullName.substring(sBeg, sEnd);
TreeNode parentNode = (TreeNode)treePath.getLastPathComponent();
TreeNode child = findChild(parentNode, s);
if (child == null) return null;
treePath = treePath.pathByAddingChild(child);
sBeg = sEnd + 1;
}
return sBeg == fullName.length() + 1 ? treePath : null;
}
private static TreeNode findChild(TreeNode parent, String name) {
for (int i = 0, numChilds = parent.getChildCount(); i < numChilds; i++) {
TreeNode child = parent.getChildAt(i);
String s;
if (child instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode)child;
Object o = node.getUserObject();
if (o instanceof Signal<?>)
s = ((Signal<?>)o).getSignalName();
else
s = o.toString();
} else {
s = child.toString();
}
if (name.equals(s)) return child;
}
return null;
}
/**
* Method to return a list of signals that are selected in an EditWindow.
* @param h a Highlighter with a selection in an EditWindow.
* @param context the VarContext of that window.
* @return a List of Signal objects in this WaveformWindow.
*/
private List<Signal<?>> findSelectedSignals(Highlighter h, VarContext context)
{
List<Signal<?>> found = new ArrayList<Signal<?>>();
// special case if a current source is selected
List<Geometric> highlightedObjects = h.getHighlightedEObjs(true, true);
if (highlightedObjects.size() == 1)
{
// if a node is highlighted that has current measured on it, use that
Geometric geom = highlightedObjects.get(0);
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
String nodeName = "I(v" + ni.getName();
for(Iterator<SignalCollection> it = sd.getSignalCollections(); it.hasNext(); )
{
SignalCollection sc = it.next();
Signal<?> sSig = sc.findSignal(TextUtils.canonicString(nodeName));
if (sSig != null)
{
found.add(sSig);
return found;
}
}
}
}
// convert all networks to signals
Set<Network> nets = h.getHighlightedNetworks();
found.addAll(findSelectedSignals(nets, context, false));
Collections.sort(found, new CompSignals());
return found;
}
private List<Signal<?>> findSelectedSignals(Set<Network> nets, VarContext context, boolean sort)
{
Cell topContext = sd.getCell();
List<Signal<?>> found = new ArrayList<Signal<?>>();
for(Network net : nets)
{
String netName = getSpiceNetName(context, net);
for(Iterator<SignalCollection> aIt = sd.getSignalCollections(); aIt.hasNext(); )
{
SignalCollection sc = aIt.next();
Signal<?> sSig = sc.findSignal(TextUtils.canonicString(netName));
if (sSig == null)
{
// try prepending the top-level cell name to the signal name
if (topContext == null) topContext = net.getParent();
String nameWithCell = topContext.getName() + "." + netName;
sSig = sc.findSignal(TextUtils.canonicString(nameWithCell));
}
if (sSig == null)
{
// when cross-probing extracted layout, hierarchy delimiter is '/x' instead of '.'
String temp = getSpiceNetName(context, net, true, false);
sSig = sc.findSignal(TextUtils.canonicString(temp));
}
if (sSig == null)
{
// when cross-probing extracted layout, hierarchy delimiter is '/' instead of '.'
String temp = getSpiceNetName(context, net, false, true);
sSig = sc.findSignal(TextUtils.canonicString(temp));
}
if (sSig == null)
{
// try prepending the top-level cell name and setting the hierarchy delimiter as '/' instead of '.'
if (topContext == null) topContext = net.getParent();
String temp = getSpiceNetName(context, net, false, true);
String nameWithCell = topContext.getName() + "." + temp;
sSig = sc.findSignal(TextUtils.canonicString(nameWithCell));
}
if (sSig == null)
{
// check for equivalent layout net name
// search up hierarchy for cell with NCC equivalent info
Cell cell = net.getParent();
NccResult result = NccCrossProbing.getResults(cell);
if (result == null)
{
for (VarContext checkContext = context; checkContext != VarContext.globalContext; checkContext = checkContext.pop())
{
cell = checkContext.getNodable().getParent();
result = NccCrossProbing.getResults(cell);
if (result != null) break;
}
}
if (result != null)
{
HierarchyEnumerator.NetNameProxy proxy = result.getEquivalence().findEquivalentNet(context, net);
if (proxy != null)
{
String otherName = getSpiceNetName(proxy.getContext(), proxy.getNet());
System.out.println("Mapped "+netName+" to "+otherName);
sSig = sc.findSignal(TextUtils.canonicString(otherName));
}
}
}
if (sSig != null)
{
List<Signal<?>> signalGroup = getSignalsFromExtractedNet(sc, sSig);
for (Signal<?> s : signalGroup)
found.add(s);
}
// else System.out.println("Can't find net "+netName+" in cell "+context.getInstPath("."));
}
}
if (sort) Collections.sort(found, new CompSignals());
return found;
}
/** Test signal lookup */
public List<Signal<?>> findAllSignals(Cell cell, VarContext context, boolean sort, boolean recurse)
{
Set<Network> nets = new HashSet<Network>();
List<Signal<?>> found = new ArrayList<Signal<?>>();
for (Iterator<Network> it = cell.getNetlist().getNetworks(); it.hasNext(); )
{
nets.add(it.next());
}
found.addAll(findSelectedSignals(nets, context, false));
if (recurse)
{
for (Iterator<Nodable> it = cell.getNetlist().getNodables(); it.hasNext(); )
{
Nodable no = it.next();
if (!(no.getProto() instanceof Cell)) continue;
Cell subCell = (Cell)no.getProto();
VarContext subContext = context.push(no);
found.addAll(findAllSignals(subCell, subContext, false, true));
}
}
if (sort) Collections.sort(found, new CompSignals());
return found;
}
private static class CompSignals implements Comparator<Signal<?>>
{
public int compare(Signal<?> s1, Signal<?> s2)
{
return TextUtils.STRING_NUMBER_ORDER.compare(s1.getFullName(), s2.getFullName());
}
}
private static Network findNetwork(Netlist netlist, String name)
{
// Should really use extended code, found in "simspicerun.cpp:sim_spice_signalname()"
for(Iterator<Network> nIt = netlist.getNetworks(); nIt.hasNext(); )
{
Network net = nIt.next();
if (getSpiceNetName(net).equalsIgnoreCase(name)) return net;
}
// try converting "@" in network names
for(Iterator<Network> nIt = netlist.getNetworks(); nIt.hasNext(); )
{
Network net = nIt.next();
String convertedName = getSpiceNetName(net).replace('@', '_');
if (convertedName.equalsIgnoreCase(name)) return net;
}
// try ignoring "_" in signal names
for(Iterator<Network> nIt = netlist.getNetworks(); nIt.hasNext(); )
{
Network net = nIt.next();
String netName = getSpiceNetName(net);
if (netName.length() != name.length()) continue;
boolean matches = true;
for(int i=0; i<netName.length(); i++)
{
char netChar = netName.charAt(i);
char nameChar = name.charAt(i);
if (nameChar == '_')
{
if (TextUtils.isLetterOrDigit(netChar)) { matches = false; break; }
} else
{
if (TextUtils.canonicChar(nameChar) != TextUtils.canonicChar(netChar)) { matches = false; break; }
}
}
if (matches) return net;
}
return null;
}
/**
* Method called when signal waveforms change, and equivalent should be shown in the edit window.
*/
public void crossProbeWaveformToEditWindow()
{
// check double-crossprobe locks
if (freezeEditWindowHighlighting) return;
freezeWaveformHighlighting = true;
// highlight the net in any associated edit windows
for(Iterator<WindowFrame> wIt = WindowFrame.getWindows(); wIt.hasNext(); )
{
WindowFrame wfr = wIt.next();
if (!(wfr.getContent() instanceof EditWindow)) continue;
EditWindow wnd = (EditWindow)wfr.getContent();
Locator loc = new Locator(wnd, this);
if (loc.getWaveformWindow() != this) continue;
VarContext context = loc.getContext();
Cell cell = wnd.getCell();
if (cell == null) continue;
Highlighter hl = wnd.getHighlighter();
Netlist netlist = cell.getNetlist();
assert netlist != null;
// if (netlist == null)
// {
// System.out.println("Sorry, a deadlock aborted crossprobing (network information unavailable). Please try again");
// freezeWaveformHighlighting = false;
// return;
// }
hl.clear();
for(Panel wp : wavePanels)
{
for(WaveSignal ws : wp.getSignals())
{
if (!ws.isHighlighted()) continue;
String want = ws.getSignal().getFullName();
Stack<Nodable> upNodables = new Stack<Nodable>();
Network net = null;
Cell subCell = cell;
VarContext subContext = context;
for (;;)
{
String contextStr = getSpiceNetName(subContext, null);
if (contextStr.length() > 0)
{
boolean matches = false;
contextStr += ".";
String altContextStr = contextStr;
if (sd.getCell() != null)
altContextStr = sd.getCell().getName().toLowerCase() + "." + contextStr;
if (want.startsWith(contextStr)) matches = true; else
{
contextStr = contextStr.replace('@', '_');
if (want.startsWith(contextStr)) matches = true;
}
if (!matches)
{
if (want.startsWith(altContextStr)) matches = true; else
{
altContextStr = altContextStr.replace('@', '_');
if (want.startsWith(altContextStr)) matches = true;
}
if (matches) contextStr = altContextStr;
}
if (!matches)
{
if (subContext == VarContext.globalContext) break;
subCell = subContext.getNodable().getParent();
upNodables.push(subContext.getNodable());
subContext = subContext.pop();
continue;
}
}
String desired = want.substring(contextStr.length());
net = findNetwork(netlist, desired);
if (net != null)
{
// found network
while (!upNodables.isEmpty())
{
Nodable no = upNodables.pop();
net = HierarchyEnumerator.getNetworkInChild(net, no);
if (net == null) break;
}
if (net != null)
hl.addNetwork(net, subCell);
break;
}
// see if this name is really a current source
if (desired.startsWith("I(v"))
{
NodeInst ni = subCell.findNode(desired.substring(3));
if (ni != null)
hl.addElectricObject(ni, subCell);
}
if (subContext == VarContext.globalContext) break;
subCell = subContext.getNodable().getParent();
upNodables.push(subContext.getNodable());
subContext = subContext.pop();
}
}
}
// also highlight anything selected in the "SIGNALS" tree
String contextStr = context.getInstPath(".");
Cell topContext = sd.getCell();
String altContextStr = contextStr;
if (topContext != null)
{
altContextStr = topContext.getName().toLowerCase();
if (contextStr.length() > 0) altContextStr += "." + contextStr;
}
ExplorerTree sigTree = wf.getExplorerTab();
Object nodeInfo = sigTree.getCurrentlySelectedObject(0);
if (nodeInfo != null && nodeInfo instanceof Signal<?>)
{
Signal<?> sig = (Signal<?>)nodeInfo;
if (sig.getSignalContext() == null || sig.getSignalContext().equals(contextStr) ||
sig.getSignalContext().equals(altContextStr))
{
String desired = sig.getSignalName();
Network net = findNetwork(netlist, desired);
if (net != null)
{
if (net.getParent() == cell)
hl.addNetwork(net, cell);
} else
{
// see if this name is really a current source
if (desired.startsWith("I(v"))
{
NodeInst ni = cell.findNode(desired.substring(3));
if (ni != null)
hl.addElectricObject(ni, cell);
}
}
}
}
hl.finished();
}
freezeWaveformHighlighting = false;
}
private Map<Network,Integer> netValues;
/**
* Method to update associated layout windows when the main cursor changes.
*/
private void updateAssociatedLayoutWindow()
{
// make sure there is a layout/schematic window being simulated
WindowFrame oWf = findSchematicsWindow();
if (oWf == null) return;
EditWindow schemWnd = (EditWindow)oWf.getContent();
boolean crossProbeChanged = schemWnd.hasCrossProbeData();
schemWnd.clearCrossProbeLevels();
Cell cell = getCell();
Netlist netlist = cell.getNetlist();
if (netlist == null)
{
System.out.println("Sorry, a deadlock aborted crossprobing (network information unavailable). Please try again");
return;
}
// reset all values on networks
netValues = new HashMap<Network,Integer>();
// assign values from simulation window traces to networks
for(Panel wp : wavePanels)
{
if (wp.isHidden()) continue;
for(WaveSignal ws : wp.getSignals())
{
Signal<?> sig = ws.getSignal();
Signal<?>[] bussedSignals = sig.getBusMembers();
if (bussedSignals != null)
{
// a digital bus trace
for(Signal<?> subSig : bussedSignals)
putValueOnTrace(subSig, netValues, netlist);
} else
{
// single signal
putValueOnTrace(sig, netValues, netlist);
}
}
}
// light up any simulation-probe objects
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.getProto() != Generic.tech().simProbeNode) continue;
Network net = null;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
net = netlist.getNetwork(con.getArc(), 0);
break;
}
if (net == null) continue;
Integer state = netValues.get(net);
if (state == null) continue;
Color col = getHighlightColor(state.intValue());
schemWnd.addCrossProbeBox(ni.getBounds(), col);
crossProbeChanged = true;
netValues.remove(net);
}
// redraw all arcs in the layout/schematic window
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
int width = netlist.getBusWidth(ai);
for(int i=0; i<width; i++)
{
Network net = netlist.getNetwork(ai, i);
Integer state = netValues.get(net);
if (state == null) continue;
Color col = getHighlightColor(state.intValue());
schemWnd.addCrossProbeLine(ai.getHeadLocation(), ai.getTailLocation(), col);
crossProbeChanged = true;
}
}
// if anything changed, queue the window for redisplay
if (crossProbeChanged)
schemWnd.repaint();
}
/**
* Method to convert a digital state to a color.
* The color is used when showing cross-probed levels in the EditWindow.
* The colors used to be user-selectable, but are not yet.
* @param state the digital state from the Waveform Window.
* @return the color to display in the EditWindow.
*/
private Color getHighlightColor(int state)
{
// determine trace color
switch (state & Stimuli.LOGIC)
{
case Stimuli.LOGIC_LOW: return new Color(User.getColor(User.ColorPrefType.WAVE_CROSS_LOW));
case Stimuli.LOGIC_HIGH: return new Color(User.getColor(User.ColorPrefType.WAVE_CROSS_HIGH));
case Stimuli.LOGIC_X: return new Color(User.getColor(User.ColorPrefType.WAVE_CROSS_UNDEF));
case Stimuli.LOGIC_Z: return new Color(User.getColor(User.ColorPrefType.WAVE_CROSS_FLOAT));
}
return Color.RED;
}
/**
* Method to crossprobe back to the schematic/layout window.
* @param sig the signal to trace back.
* @param netValues a Map to store state by Network.
* @param netlist the netlist with the signal in it.
*/
private void putValueOnTrace(Signal<?> sig, Map<Network,Integer> netValues, Netlist netlist)
{
// set simulation value on the network in the associated layout/schematic window
Network net = findNetwork(netlist, sig.getSignalName());
if (net == null) return;
// find the proper data for the main cursor
Signal.View<?> view = sig.getExactView();
int numEvents = view.getNumEvents();
int state = Stimuli.LOGIC_X;
for(int i=numEvents-1; i>=0; i--)
{
double xValue = view.getTime(i);
if (xValue <= mainXPosition)
{
Sample samp = view.getSample(i);
if (!(samp instanceof DigitalSample)) return;
DigitalSample ds = (DigitalSample)samp;
if (ds.isLogic0()) state = Stimuli.LOGIC_LOW; else
if (ds.isLogic1()) state = Stimuli.LOGIC_HIGH; else
if (ds.isLogicZ()) state = Stimuli.LOGIC_Z;
break;
}
}
netValues.put(net, new Integer(state));
}
/**
* Method called when the main or extension cursors should be centered.
* @param main true for the main cursor, false for the extension cursor.
*/
public void centerCursor(boolean main)
{
boolean havePanel = false;
double lowXValue = 0, highXValue = 0;
for(Panel wp : wavePanels)
{
double low = wp.getMinXAxis();
double high = wp.getMaxXAxis();
if (havePanel)
{
lowXValue = Math.max(lowXValue, low);
highXValue = Math.min(highXValue, high);
} else
{
lowXValue = low;
highXValue = high;
havePanel = true;
}
}
if (!havePanel) return;
double center = (lowXValue + highXValue) / 2;
if (main) setMainXPositionCursor(center); else
setExtensionXPositionCursor(center);
for(Panel wp : wavePanels)
{
wp.repaintWithRulers();
}
}
// ************************************* STIMULI CONTROL *************************************
/**
* Method to update the Simulation data for this waveform window.
* When new data is read from disk, this is used.
* @param sd new simulation data for this window.
*/
public void setSimData(Stimuli sd)
{
if (this.sd != null)
this.sd.finished();
this.sd = sd;
// reload the sweeps
resetSweeps();
// adjust the overall X axis signal (if it is not time)
Signal<?> oldXAxisSignalAll = null;
if (xAxisSignalAll != null)
{
oldXAxisSignalAll = xAxisSignalAll;
xAxisSignalAll = null;
SignalCollection sc = oldXAxisSignalAll.getSignalCollection();
for(Signal<?> newSs : sc.getSignals())
{
String newSigName = newSs.getFullName();
if (!newSigName.equals(oldXAxisSignalAll.getFullName())) continue;
xAxisSignalAll = newSs;
break;
}
if (xAxisSignalAll == null)
System.out.println("Could not find main X axis signal " + oldXAxisSignalAll.getFullName() +
" in the new data");
}
List<Panel> panelList = new ArrayList<Panel>();
for(Panel wp : wavePanels)
panelList.add(wp);
for(Panel wp : panelList)
{
boolean redoPanel = false;
// adjust the panel's X axis signal (if it is not time)
if (wp.getXAxisSignal() != null)
{
Signal<?> oldSig = wp.getXAxisSignal();
wp.setXAxisSignal(null);
String oldSigName = oldSig.getFullName();
SignalCollection sc = oldSig.getSignalCollection();
for(Signal<?> newSs : sc.getSignals())
{
String newSigName = newSs.getFullName();
if (!newSigName.equals(oldSigName)) continue;
wp.setXAxisSignal(newSs);
break;
}
if (wp.getXAxisSignal() == null)
{
System.out.println("Could not find X axis signal " + oldSigName + " in the new data");
redoPanel = true;
}
}
// adjust all signals inside the panel
for(WaveSignal ws : wp.getSignals())
{
// find the signal name in the new list
Signal<?> ss = ws.getSignal();
String oldSigName = ss.getFullName();
ws.setSignal(null);
SignalCollection sc = ss.getSignalCollection();
for(Signal<?> newSs : sc.getSignals())
{
String newSigName = newSs.getFullName();
if (!newSigName.equals(oldSigName)) continue;
ws.setSignal(newSs);
break;
}
if (ws.getSignal() == null)
{
System.out.println("Could not find signal " + oldSigName + " in the new data");
redoPanel = true;
}
}
while (redoPanel)
{
redoPanel = false;
for(WaveSignal ws : wp.getSignals())
{
Signal<?> s = ws.getSignal();
if (s == null)
{
redoPanel = true;
if (wp.getSignalButtons() != null)
wp.removeSignal(ws.getButton());
break;
}
}
}
if (wp.getNumSignals() == 0)
{
// removed all signals: delete the panel
wp.getWaveWindow().closePanel(wp);
} else
{
if (wp.getSignalButtons() != null)
{
wp.getSignalButtons().validate();
wp.getSignalButtons().repaint();
}
wp.repaintContents();
}
}
wf.wantToRedoSignalTree();
if (sd.getEngine() != null)
System.out.println("Simulation data reloaded from circuit"); else
System.out.println("Simulation data reloaded from disk");
}
public static WaveformWindow getCurrentWaveformWindow()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Must select a Waveform window first");
return null;
}
return (WaveformWindow)content;
}
/**
* Method to write the simulation data as a tab-separated file.
*/
public static void exportSimulationData()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Must select a Waveform window first");
return;
}
WaveformWindow ww = (WaveformWindow)content;
String configurationFileName = OpenFile.chooseOutputFile(FileType.TEXT, "Waveform Export File", "wavedata.txt");
if (configurationFileName == null) return;
try
{
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(configurationFileName)));
List<Signal<?>> dumpSignals = new ArrayList<Signal<?>>();
List<Integer> dumpSweeps = new ArrayList<Integer>();
List<Signal<?>> dumpWaveforms = new ArrayList<Signal<?>>();
for(Panel wp : ww.wavePanels)
{
if (wp.isHidden()) continue;
Signal<?> signalInX = ww.xAxisSignalAll;
if (!ww.xAxisLocked) signalInX = wp.getXAxisSignal();
if (signalInX != null) addSignalSweep(signalInX, -1, dumpSignals, dumpSweeps, dumpWaveforms);
for(WaveSignal ws : wp.getSignals())
addSignalSweep(ws.getSignal(), -1, dumpSignals, dumpSweeps, dumpWaveforms);
}
int numEntries = dumpSignals.size() + 1;
String [] entries = new String[numEntries];
entries[0] = "TIME";
for(int i=1; i<numEntries; i++)
{
Signal<?> sig = dumpSignals.get(i-1);
entries[i] = sig.getFullName();
int s = dumpSweeps.get(i-1).intValue();
if (s >= 0)
{
entries[i] += "/S=" + s;
}
}
for(int i=0; i<numEntries; i++)
{
if (i > 0) printWriter.print("\t");
printWriter.print(entries[i]);
}
printWriter.println();
for(int j=0; ; j++)
{
// get signal values for this iteration
boolean haveData = false;
entries[0] = null;
for(int i=1; i<numEntries; i++)
{
entries[i] = "";
Signal<?> sig = dumpSignals.get(i-1);
Signal.View<?> view = sig.getExactView();
if (j < view.getNumEvents())
{
Sample sample = view.getSample(j);
if (sample != null)
{
if (sample instanceof ScalarSample)
{
double t = view.getTime(j);
double v = ((ScalarSample)sample).getValue();
if (entries[0] == null) entries[0] = "" + t;
entries[i] = "" + v;
haveData = true;
} else if (sample instanceof DigitalSample)
{
if (entries[0] == null) entries[0] = "" + view.getTime(j);
entries[i] = "" + DigitalSample.getState((Signal.View<DigitalSample>)view, j);
haveData = true;
}
}
}
}
if (!haveData) break;
if (entries[0] == null) entries[0] = "";
for(int i=0; i<numEntries; i++)
{
if (i > 0) printWriter.print("\t");
printWriter.print(entries[i]);
}
printWriter.println();
}
printWriter.close();
} catch (IOException e)
{
System.out.println("Error writing configuration");
return;
}
System.out.println("Wrote " + configurationFileName);
}
private static void addSignalSweep(Signal<?> sig, int s, List<Signal<?>> dumpSignals, List<Integer> dumpSweeps, List<Signal<?>> waveforms)
{
for(int i=0; i<dumpSignals.size(); i++)
{
if (dumpSignals.get(i) == sig && dumpSweeps.get(i).intValue() == s) return;
}
dumpSignals.add(sig);
dumpSweeps.add(new Integer(s));
waveforms.add(sig);
}
/**
* Method to refresh simulation data by menu in ToolMenu. This would allow to attach a KeyBinding
*/
public static void refreshSimulationData()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Nothing to refresh in non Waveform window");
return; // nothing to do
}
((WaveformWindow)content).refreshData();
}
/**
* Method to clear all panels from the waveform window.
*/
public static void clearSimulationData()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Nothing to refresh in non Waveform window");
return; // nothing to do
}
WaveformWindow ww = (WaveformWindow)content;
// clear the display
ww.clearAllPanels();
ww.saveSignalOrder();
}
/**
* Method to remove all panels from the display.
*/
public void clearAllPanels()
{
List<Panel> closeList = new ArrayList<Panel>();
for(Panel wp : wavePanels)
closeList.add(wp);
for(Panel wp : closeList)
{
closePanel(wp);
}
}
/**
* Method to refresh the simulation data from disk.
*/
private void refreshData()
{
// // if there is no stimuli file, simulator is built-in: update it
// if (sd.getFileURL() == null)
// {
// sd.getEngine().update();
// return;
// }
// if there is a simulation engine (i.e. IRISM, ALS) ask simulator to reload circuit
if (sd.getEngine() != null)
{
sd.getEngine().refresh();
return;
}
// there is no simulation engine (i.e. external Spice, Verilog) reload external data
SimulationData.plot(sd.getCell(), sd.getFileURL(), this);
}
/**
* Method to save the waveform window configuration to a disk file.
*/
public static void saveConfiguration()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
WaveformWindow ww = findWaveformWindow(cell);
if (ww == null)
{
System.out.println("There is no waveform window to save");
return;
}
String configurationFileName = OpenFile.chooseOutputFile(FileType.TEXT, "Waveform Configuration File", "waveform.txt");
if (configurationFileName == null) return;
try
{
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(configurationFileName)));
for(int i=0; i<ww.wavePanels.size(); i++)
{
Panel wp = ww.wavePanels.get(i);
if (wp.isHidden()) continue;
boolean first = true;
for(WaveSignal ws : wp.getSignals())
{
String sigName = ws.getSignal().getFullName();
if (first)
{
// header
first = false;
String collectionName = "";
//if (wp.getAnalysisType() != null) collectionName = " " + wp.getAnalysisType();
String log = "";
if (wp.isPanelLogarithmicHorizontally()) log = " xlog";
if (wp.isPanelLogarithmicVertically()) log += " ylog";
if (i > 0) printWriter.println();
printWriter.println("panel" + collectionName + log);
printWriter.println("zoom " + wp.getYAxisLowValue() + " " + wp.getYAxisHighValue() +
" " + wp.getMinXAxis() + " " + wp.getMaxXAxis());
Signal<?> signalInX = ww.xAxisSignalAll;
if (!ww.xAxisLocked) signalInX = wp.getXAxisSignal();
if (signalInX != null) printWriter.println("x-axis " + signalInX.getFullName());
}
Color color = ws.getColor();
printWriter.println("signal " + sigName + " " + color.getRed() + "," + color.getGreen() + "," + color.getBlue());
}
}
printWriter.close();
} catch (IOException e)
{
System.out.println("Error writing configuration");
return;
}
System.out.println("Wrote " + configurationFileName);
}
/**
* Method to restore the waveform window configuration from a disk file.
*/
public static void restoreConfiguration()
{
Cell cell = WindowFrame.needCurCell();
if (cell == null) return;
WaveformWindow ww = findWaveformWindow(cell);
if (ww == null)
{
System.out.println("There is no waveform window to restore");
return;
}
String configurationFileName = OpenFile.chooseInputFile(FileType.TEXT, "Waveform Configuration File");
if (configurationFileName == null) return;
// clear the display
ww.clearAllPanels();
// read the file
URL url = TextUtils.makeURLToFile(configurationFileName);
Panel curPanel = null;
try
{
URLConnection urlCon = url.openConnection();
InputStreamReader is = new InputStreamReader(urlCon.getInputStream());
LineNumberReader lineReader = new LineNumberReader(is);
for(;;)
{
String buf = lineReader.readLine();
if (buf == null) break;
String [] keywords = buf.split(" ");
if (keywords.length == 0) continue;
if (keywords[0].equals("panel"))
{
boolean xLog = false, yLog = false;
for(int i=1; i<keywords.length; i++)
{
if (keywords[i].equals("xlog")) xLog = true; else
if (keywords[i].equals("ylog")) yLog = true;
}
int height = User.getWaveformDigitalPanelHeight();
if (ww.getSimData().isAnalog()) height = User.getWaveformAnalogPanelHeight();
curPanel = new Panel(ww, height);
if (xLog)
{
if (ww.isXAxisLocked()) ww.togglePanelXAxisLock();
curPanel.setPanelLogarithmicHorizontally(true);
}
if (yLog) curPanel.setPanelLogarithmicVertically(true);
continue;
}
if (keywords[0].equals("zoom"))
{
if (curPanel == null) continue;
double lowYValue = TextUtils.atof(keywords[1]);
double highYValue = TextUtils.atof(keywords[2]);
double lowXValue = TextUtils.atof(keywords[3]);
double highXValue = TextUtils.atof(keywords[4]);
curPanel.setXAxisRange(lowXValue, highXValue);
curPanel.setYAxisRange(lowYValue, highYValue);
continue;
}
if (keywords[0].equals("x-axis"))
{
if (curPanel == null) continue;
Stimuli sd = ww.getSimData();
SignalCollection sc = sd.getSignalCollections().next();
if (sc == null) continue;
Signal<?> sig = findSignalForNetwork(sc, keywords[1]);
if (sig == null) continue;
if (ww.isXAxisLocked()) ww.togglePanelXAxisLock();
curPanel.setXAxisSignal(sig);
continue;
}
if (keywords[0].equals("signal"))
{
if (curPanel == null) continue;
Stimuli sd = ww.getSimData();
SignalCollection sc = sd.getSignalCollections().next();
if (sc == null) continue;
Signal<?> sig = findSignalForNetwork(sc, keywords[1]);
if (sig == null) continue;
String [] colorNames = keywords[2].split(",");
int red = TextUtils.atoi(colorNames[0]);
int green = TextUtils.atoi(colorNames[1]);
int blue = TextUtils.atoi(colorNames[2]);
Color color = new Color(red, green, blue);
WaveSignal ws = new WaveSignal(curPanel, sig);
ws.setColor(color);
continue;
}
}
lineReader.close();
for(Panel panel : ww.wavePanels)
{
panel.repaintWithRulers();
}
ww.saveSignalOrder();
} catch (IOException e)
{
System.out.println("Error reading " + configurationFileName);
return;
}
}
private static Map<CellId,String> savedSignalOrder = new HashMap<CellId,String>();
/**
* Method to save the signal ordering on the cell.<BR>
*
* Saved signals are stored in the Preferences as a single string.
* The string is located in com.sun.electric.database.hierarchy.<LibraryName>
* in a Preference named SavedSignalsForCell<CellName>.<BR>
*
* The format of the string is as follows:
* Each panel in the waveform window is terminated by "\n", so for example two panels would look like this:<BR>
* <PanelInfo> \n <PanelInfo> \n<BR>
* Each PanelInfo section starts with information about the SignalCollection in that panel,
* and then lists the signals in that panel. The format is:<BR>
* \t [<SignalCollectionName>] [(<HorizontalSignal>)] <SignalList><BR>
* Where <SignalList> is one or more of this:<BR>
* \t <SignalName> { <Red> , <Green> , <Blue> }<BR>
*
* Example:<BR>
* \t\t1:net_198 {255,0,0}\n<BR>
* This has just one \n in it so it defines a single panel.
* The first \t introduces the SignalCollection which is blank, meaning that it is
* a Transient analysis and has Time as the horizontal axis.
* There is just one signal listed (1:net_198) and its color is red (255,0,0).
*/
public void saveSignalOrder()
{
Cell cell = getCell();
if (cell == null) return;
StringBuffer sb = new StringBuffer();
for(Panel wp : wavePanels)
{
boolean first = true;
for(WaveSignal ws : wp.getSignals())
{
String sigName = ws.getSignal().getFullName();
if (first)
{
// header begins with a tab
sb.append("\t");
Signal<?> signalInX = xAxisSignalAll;
if (!xAxisLocked) signalInX = wp.getXAxisSignal();
first = false;
if (signalInX != null) sb.append("(" + signalInX.getFullName() + ")");
}
sb.append("\t");
sb.append(sigName);
Color color = ws.getColor();
sb.append(" {" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + "}");
}
sb.append("\n");
}
savedSignalOrder.put(cell.getId(), sb.toString());
}
/**
* Method called when the program exits to preserve signal ordering in cells.
*/
public static void preserveSignalOrder()
{
Pref.delayPrefFlushing();
for(Map.Entry<CellId,String> e: savedSignalOrder.entrySet())
{
CellId cellId = e.getKey();
String savedOrder = e.getValue();
Preferences libPrefs = Pref.getLibraryPreferences(cellId.libId);
String key = "SavedSignalsForCell" + cellId.cellName.getName();
if (savedOrder.length() == 0)
libPrefs.remove(key);
else
libPrefs.put(key, savedOrder);
}
Pref.resumePrefFlushing();
}
/**
* Method to get the saved signal information for a cell.
* @param cell the Cell to query.
* @return a list of strings, one per waveform window panel, with tab-separated signal names in that panel.
* Returns an empty array if nothing is saved.
*/
public static String [] getSignalOrder(Cell cell)
{
CellId cellId = cell.getId();
String savedOrder = savedSignalOrder.get(cellId);
if (savedOrder == null)
{
Preferences libPrefs = Pref.getLibraryPreferences(cellId.libId);
String key = "SavedSignalsForCell" + cellId.cellName.getName();
savedOrder = libPrefs.get(key, "");
if (savedOrder.length() == 0) return new String[0];
}
// convert a single string into an array of strings
List<String> panels = new ArrayList<String>();
int startPos = 0;
for(;;)
{
int endCh = savedOrder.indexOf('\n', startPos);
if (endCh < 0) break;
String panel = savedOrder.substring(startPos, endCh);
panels.add(panel);
startPos = endCh + 1;
}
String [] ret = new String[panels.size()];
int i=0;
for(String s : panels)
ret[i++] = s;
return ret;
}
// ************************************* DISPLAY CONTROL *************************************
public Font getFont() { return waveWindowFont; }
public FontRenderContext getFontRenderContext() { return waveWindowFRC; }
public Color getOffStrengthColor() { return offStrengthColor; }
public Color getNodeStrengthColor() { return nodeStrengthColor; }
public Color getGateStrengthColor() { return gateStrengthColor; }
public Color getPowerStrengthColor() { return powerStrengthColor; }
/**
* Method called to toggle the display of vertex points.
*/
public void toggleShowPoints()
{
linePointMode = (linePointMode+1) % 3;
switch (linePointMode)
{
case 0: showPoints.setIcon(iconLineOnPointOff); break;
case 1: showPoints.setIcon(iconLineOnPointOn); break;
case 2: showPoints.setIcon(iconLineOffPointOn); break;
}
for(Panel wp : wavePanels)
{
wp.repaintWithRulers();
}
}
/**
* Method to return the drawing mode for analog waves.
* @return the drawing mode for analog waves.
* 0 means draw lines only; 1 means draw lines and points; 2 means draw points only.
*/
public int getLinePointMode() { return linePointMode;}
/**
* Method called to toggle the display of a grid.
*/
public void toggleGridPoints()
{
showGrid = !showGrid;
for(Panel wp : wavePanels)
{
wp.repaintWithRulers();
}
}
public boolean isShowGrid() { return showGrid; }
/**
* Method to add a signal to the display.
* Called when the user double-clicks on the signal in the explorer tree.
* @param sig the Signal to add to the display
*/
public void addSignal(Signal<?> sig)
{
// add signal on top of current panel
Signal<?> as = sig;
boolean found = false;
if (!sig.isDigital())
{
for(Panel wp : wavePanels)
{
if (wp.isSelected())
{
WaveSignal.addSignalToPanel(sig, wp, null);
if (getMainHorizRuler() != null)
getMainHorizRuler().repaint();
found = true;
break;
}
}
}
if (!found)
{
// create a new panel for the signal
Panel wp = makeNewPanel(-1);
wp.fitToSignal(as);
if (!xAxisLocked)
wp.setXAxisRange(as.getMinTime(), as.getMaxTime());
WaveSignal.addSignalToPanel(sig, wp, null);
if (getMainHorizRuler() != null)
getMainHorizRuler().repaint();
}
overall.validate();
saveSignalOrder();
}
/**
* Method called when "delete" command (or key) is given.
* If a control point is selected, delete it.
* If a single signal is selected, remove it.
*/
public void deleteSelectedSignals()
{
for(Panel wp : wavePanels)
{
if (!wp.isSelected()) continue;
boolean removedSingleStimuli = false;
for(WaveSignal ws : wp.getSignals())
{
if (ws.getSelectedControlPoints() != null)
{
if (sd.getEngine() != null)
{
if (sd.getEngine().removeSelectedStimuli())
removedSingleStimuli = true;
}
}
}
if (!removedSingleStimuli) deleteSignalFromPanel(wp);
break;
}
}
/**
* Method to make the waveform window/panel fill in X only.
*/
public static void fillInX()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Must select a Waveform window first");
return;
}
WaveformWindow ww = (WaveformWindow)content;
ww.fillWaveform(1);
}
/**
* Method to make the waveform window/panel fill in Y only.
*/
public static void fillInY()
{
WindowFrame current = WindowFrame.getCurrentWindowFrame();
WindowContent content = current.getContent();
if (!(content instanceof WaveformWindow))
{
System.out.println("Must select a Waveform window first");
return;
}
WaveformWindow ww = (WaveformWindow)content;
ww.fillWaveform(2);
}
/**
* Method to make the waveform window/panel fill in X and Y.
*/
public void fillScreen()
{
fillWaveform(3);
}
/**
* Method to make the stimuli fill the waveform window.
* @param how what to fill: 1=X, 2=Y, 3=both.
*/
private void fillWaveform(int how)
{
// accumulate bounds for all displayed panels
double leftEdge=0, rightEdge=0;
Map<Panel,Double> panelLefts = new HashMap<Panel,Double>();
Map<Panel,Double> panelRights = new HashMap<Panel,Double>();
for(Panel wp : wavePanels)
{
double panelLeft = 0, panelRight = 0;
for(WaveSignal ws : wp.getSignals())
{
if (panelLeft == panelRight)
{
panelLeft = ws.getSignal().getMinTime();
panelRight = ws.getSignal().getMaxTime();
} else
{
panelLeft = Math.min(panelLeft, ws.getSignal().getMinTime());
panelRight = Math.max(panelRight, ws.getSignal().getMaxTime());
}
}
panelLefts.put(wp, new Double(panelLeft));
panelRights.put(wp, new Double(panelRight));
if (leftEdge == rightEdge)
{
leftEdge = panelLeft;
rightEdge = panelRight;
} else
{
leftEdge = Math.min(leftEdge, panelLeft);
rightEdge = Math.max(rightEdge, panelRight);
}
}
// if there is an overriding signal on the X axis, use its bounds
if (xAxisLocked && xAxisSignalAll != null)
{
leftEdge = xAxisSignalAll.getMinTime();
rightEdge = xAxisSignalAll.getMaxTime();
}
for(Panel wp : wavePanels)
{
if (!xAxisLocked && !wp.isSelected()) continue;
double useLeft = leftEdge, useRight = rightEdge;
if (!xAxisLocked)
{
useLeft = panelLefts.get(wp).doubleValue();
useRight = panelRights.get(wp).doubleValue();
}
if ((how&2)!=0) wp.fitToSignal(null);
if (useLeft != useRight && (wp.getMinXAxis() != useLeft || wp.getMaxXAxis() != useRight) && (how&1) != 0)
{
wp.setXAxisRange(useLeft, useRight);
wp.repaintWithRulers();
}
}
}
public void zoomOutContents()
{
for(Panel wp : wavePanels)
{
if (!xAxisLocked && !wp.isSelected()) continue;
boolean timeInXAxis = true;
if (xAxisLocked)
{
if (xAxisSignalAll != null) timeInXAxis = false;
} else
{
if (wp.getXAxisSignal() != null) timeInXAxis = false;
}
double range = wp.getMaxXAxis() - wp.getMinXAxis();
wp.setXAxisRange(wp.getMinXAxis() - range/2, wp.getMaxXAxis() + range/2);
if (wp.getMinXAxis() < 0 && timeInXAxis)
{
wp.setXAxisRange(0, wp.getMaxXAxis() - wp.getMinXAxis());
}
wp.repaintWithRulers();
}
}
public void zoomInContents()
{
for(Panel wp : wavePanels)
{
if (!xAxisLocked && !wp.isSelected()) continue;
double range = wp.getMaxXAxis() - wp.getMinXAxis();
wp.setXAxisRange(wp.getMinXAxis() + range/4, wp.getMaxXAxis() - range/4);
wp.repaintWithRulers();
}
}
public void focusOnHighlighted()
{
if (mainXPosition == extXPosition) return;
double maxXPosition, minXPosition;
if (mainXPosition > extXPosition)
{
double size = (mainXPosition-extXPosition) / 20.0;
maxXPosition = mainXPosition + size;
minXPosition = extXPosition - size;
} else
{
double size = (extXPosition-mainXPosition) / 20.0;
maxXPosition = extXPosition + size;
minXPosition = mainXPosition - size;
}
for(Panel wp : wavePanels)
{
if (!xAxisLocked && !wp.isSelected()) continue;
if (wp.getMinXAxis() != minXPosition || wp.getMaxXAxis() != maxXPosition)
{
if (wp.getMinXAxis() > wp.getMaxXAxis()) wp.setXAxisRange(maxXPosition, minXPosition); else
wp.setXAxisRange(minXPosition, maxXPosition);
wp.repaintWithRulers();
}
}
}
public boolean isWaveWindowLogarithmic() { return mainHorizRulerPanelLogarithmic; }
public void setWaveWindowLogarithmic(boolean logarithmic)
{
mainHorizRulerPanelLogarithmic = logarithmic;
mainHorizRulerPanel.repaint();
}
// ************************************* DRAG AND DROP CLASSES *************************************
/**
* This class extends JPanel so that components of the Waveform window can be identified by the Drag and Drop system
* and by the key binding manager.
*/
public static class OnePanel extends JPanel
{
Panel panel;
WaveformWindow ww;
public OnePanel(Panel panel, WaveformWindow ww)
{
super();
this.panel = panel;
this.ww = ww;
}
public Panel getPanel() { return panel; }
public WaveformWindow getWaveformWindow() { return ww; }
}
private static class WaveFormDropTarget implements DropTargetListener
{
public void dragEnter(DropTargetDragEvent e)
{
e.acceptDrag(e.getDropAction());
}
public void dragOver(DropTargetDragEvent e)
{
e.acceptDrag(e.getDropAction());
}
public void dropActionChanged(DropTargetDragEvent e)
{
e.acceptDrag(e.getDropAction());
}
public void dragExit(DropTargetEvent e) {}
/**
* Entry point when something is dropped onto the waveform window.
* The data that is transferred is always a string with these values:
*
* "PANEL #" means an entire panel has been dragged (user has rearranged panels)
* Drag originates in Panel.DragLabel.dragGestureRecognized().
*
* "TRANS sig" / "DC sig" / "AC sig" / "MEASUREMENT sig" means
* a transient/DC/AC/measurement signal has been dragged (from the Explorer tree)
* Drag originates in ExplorerTree.dragGestureRecognized().
* Multiple signals may be combined, separated by Newline.
*
* "PANEL # MOVEBUTTON sig" / "PANEL # COPYBUTTON sig" means a signal has been
* moved or copied (from one panel to another).
* Drag originates in DragButton.dragGestureRecognized().
*/
public void drop(DropTargetDropEvent dtde)
{
// get information about the drop (such as the signal name)
Object data = null;
try
{
dtde.acceptDrop(DnDConstants.ACTION_LINK);
data = dtde.getTransferable().getTransferData(DataFlavor.stringFlavor);
if (data == null)
{
dtde.dropComplete(false);
return;
}
} catch (Throwable t)
{
ActivityLogger.logException(t);
dtde.dropComplete(false);
return;
}
if (!(data instanceof String))
{
dtde.dropComplete(false);
return;
}
String sigNameData = (String)data;
String [] sigNames = sigNameData.split("\n");
String signalCollectionName = null;
for(int i=0; i<sigNames.length; i++)
{
String collectionName = "SIGNALS";
String aSigName = sigNames[i];
if (aSigName.startsWith("TRANS "))
{
sigNames[i] = aSigName.substring(6);
collectionName = "TRANS SIGNALS";
} else if (aSigName.startsWith("MEASUREMENT "))
{
sigNames[i] = aSigName.substring(12);
collectionName = "MEASUREMENTS";
} else if (aSigName.startsWith("AC "))
{
sigNames[i] = aSigName.substring(3);
collectionName = "AC SIGNALS";
} else if (aSigName.startsWith("DC "))
{
sigNames[i] = aSigName.substring(3);
collectionName = "DC SIGNALS";
}
if (signalCollectionName == null) signalCollectionName = collectionName; else
{
if (signalCollectionName != collectionName)
{
Job.getUserInterface().showErrorMessage("All signals must be the same type", "Incorrect Signal Selection");
dtde.dropComplete(false);
return;
}
}
}
if (signalCollectionName == null)
{
dtde.dropComplete(false);
return;
}
// see if the signal was dropped onto a ruler panel (setting x-axis)
DropTarget dt = (DropTarget)dtde.getSource();
if (dt.getComponent() instanceof HorizRuler)
{
// dragged a signal to the ruler panel: make sure only one signal was selected
if (sigNames.length != 1)
{
Job.getUserInterface().showErrorMessage("Only one signal can be dragged to a ruler", "Too Much Selected");
dtde.dropComplete(false);
return;
}
HorizRuler hr = (HorizRuler)dt.getComponent();
Panel panel = hr.getPanel();
WaveformWindow ww = hr.getWaveformWindow();
// find the signal that was dragged
Signal<?> sSig = null;
if (sigNames[0].startsWith("PANEL "))
{
// get signal when dragged from inside the waveform window
int sigPos = Math.max(sigNames[0].indexOf("MOVEBUTTON "), sigNames[0].indexOf("COPYBUTTON "));
if (sigPos >= 0)
{
// dragging from waveform window signal to horizontal ruler
int panelNumber = TextUtils.atoi(sigNames[0].substring(6));
Panel sourcePanel = ww.getPanelFromNumber(panelNumber);
String signalName = sigNames[0].substring(sigPos + 11);
for(WaveSignal ws : sourcePanel.getSignals())
{
if (!ws.getSignal().getFullName().equals(signalName)) continue;
sSig = ws.getSignal();
break;
}
signalCollectionName = sSig.getSignalCollection().getName();
}
} else
{
SignalCollection sc = ww.getSimData().findSignalCollection(signalCollectionName);
if (sc == null)
{
System.out.println("Cannot find " + signalCollectionName + " data");
dtde.dropComplete(true);
return;
}
sSig = ww.findSignal(sigNames[0], sc);
}
if (sSig != null)
{
if (panel == null)
{
// dropped signal onto main time ruler
ww.xAxisSignalAll = sSig;
for(Panel wp : ww.wavePanels)
wp.setXAxisRange(sSig.getMinTime(), sSig.getMaxTime());
ww.redrawAllPanels();
} else
{
// dropped signal onto a single panel's time ruler
panel.setXAxisSignal(sSig);
panel.setXAxisRange(sSig.getMinTime(), sSig.getMaxTime());
panel.repaintContents();
}
hr.repaint();
ww.saveSignalOrder();
}
dtde.dropComplete(false);
return;
}
// determine which panel was the target of the drop
WaveformWindow ww = null;
Panel panel = null;
if (dt.getComponent() instanceof Panel)
{
panel = (Panel)dt.getComponent();
ww = panel.getWaveWindow();
}
if (dt.getComponent() instanceof OnePanel)
{
OnePanel op = (OnePanel)dt.getComponent();
ww = op.getWaveformWindow();
panel = op.getPanel();
}
if (dt.getComponent() instanceof WaveTable)
{
WaveTable table = (WaveTable)dt.getComponent();
ww = table.ww;
int row = table.rowAtPoint(dtde.getLocation());
if (row != -1)
{
Object obj = ww.tableModel.getValueAt(row, 0);
OnePanel op = (OnePanel)obj;
panel = op.getPanel();
}
}
if (panel == null)
{
dtde.dropComplete(false);
return;
}
// see if rearranging the waveform window
if (sigNames[0].startsWith("PANEL "))
{
// rearranging signals and panels
int panelNumber = TextUtils.atoi(sigNames[0].substring(6));
Panel sourcePanel = ww.getPanelFromNumber(panelNumber);
if (sourcePanel == panel)
{
// moved to same panel
dtde.dropComplete(false);
return;
}
// see if a signal button was grabbed
int sigMovePos = sigNames[0].indexOf("MOVEBUTTON ");
int sigCopyPos = sigNames[0].indexOf("COPYBUTTON ");
if (sigMovePos < 0 && sigCopyPos < 0)
{
// moving the entire panel
ww.stopEditing();
ww.wavePanels.remove(sourcePanel);
int destIndex = ww.wavePanels.indexOf(panel);
if (dtde.getLocation().y > panel.getBounds().height/2)
destIndex++;
ww.wavePanels.add(destIndex, sourcePanel);
ww.reloadTable();
ww.table.repaint();
ww.getPanel().validate();
dtde.dropComplete(true);
ww.saveSignalOrder();
return;
}
// moving/copying a signal
int sigPos = Math.max(sigMovePos, sigCopyPos);
String signalName = sigNames[0].substring(sigPos + 11);
Signal<?> sSig = null;
Color oldColor = null;
for(WaveSignal ws : sourcePanel.getSignals())
{
if (!ws.getSignal().getFullName().equals(signalName)) continue;
sSig = ws.getSignal();
oldColor = ws.getColor();
if (sigCopyPos < 0)
{
sourcePanel.removeHighlightedSignal(ws, true);
sourcePanel.removeSignal(ws.getButton());
}
break;
}
if (sSig != null)
{
sourcePanel.getSignalButtons().validate();
sourcePanel.getSignalButtons().repaint();
sourcePanel.repaintContents();
WaveSignal.addSignalToPanel(sSig, panel, oldColor);
}
ww.saveSignalOrder();
dtde.dropComplete(true);
return;
}
// not rearranging: dropped a signal onto a panel
SignalCollection sc = ww.getSimData().findSignalCollection(signalCollectionName);
for(int i=0; i<sigNames.length; i++)
{
Signal<?> sSig = ww.findSignal(sigNames[i], sc);
if (sSig == null)
{
dtde.dropComplete(false);
return;
}
if (panel != null) {
// overlay this signal onto an existing panel
WaveSignal.addSignalToPanel(sSig, panel, null);
panel.makeSelectedPanel(-1, -1);
continue;
}
// add this signal in a new panel
panel = ww.makeNewPanel(-1);
panel.fitToSignal(sSig);
new WaveSignal(panel, sSig);
}
ww.overall.validate();
panel.repaintContents();
panel.getWaveWindow().saveSignalOrder();
dtde.dropComplete(true);
}
}
// ************************************* CLASS TO ASSOCIATE WAVEFORM WINDOWS WITH EDIT WINDOWS *************************************
/**
* Class to find the WaveformWindow associated with the cell in a given EditWindow.
* May have to climb the hierarchy to find the top-level cell that is being simulated.
*/
public static class Locator
{
private WaveformWindow ww;
private VarContext context;
/**
* The constructor takes an EditWindow and locates the associated WaveformWindow.
* It may have to climb the hierarchy to find it.
* @param wnd the EditWindow that is being simulated.
*/
public Locator(EditWindow wnd)
{
Cell cellInWindow = wnd.getCell();
VarContext curContext = wnd.getVarContext();
ww = null;
Stack<Nodable> contextStack = new Stack<Nodable>();
for(;;)
{
ww = WaveformWindow.findWaveformWindow(cellInWindow);
if (ww != null) break;
Nodable no = curContext.getNodable();
if (no == null) break;
contextStack.push(no);
cellInWindow = no.getParent();
curContext = curContext.pop();
//context = no.getName() + "." + context;
}
context = VarContext.globalContext;
while (!contextStack.isEmpty()) {
context = context.push(contextStack.pop());
}
}
/**
* The constructor takes an EditWindow and a WaveformWindow and determines whether they are associated.
* It may have to climb the hierarchy to find out.
* @param wnd the EditWindow that is being simulated.
* @param wantWW the WaveformWindow that is being associated.
*/
public Locator(EditWindow wnd, WaveformWindow wantWW)
{
Cell cellInWindow = wnd.getCell();
VarContext curContext = wnd.getVarContext();
ww = null;
Stack<Nodable> contextStack = new Stack<Nodable>();
for(;;)
{
if (wantWW.getCell() == cellInWindow) { ww = wantWW; break; }
Nodable no = curContext.getNodable();
if (no == null) break;
contextStack.push(no);
cellInWindow = no.getParent();
curContext = curContext.pop();
}
context = VarContext.globalContext;
while (!contextStack.isEmpty()) {
context = context.push(contextStack.pop());
}
}
/**
* Method to return the WaveformWindow found by this locator class.
* @return the WaveformWindow associated with the EditWindow given to the constructor.
* Returns null if no WaveformWindow could be found.
*/
public WaveformWindow getWaveformWindow() { return ww; }
/**
* Method to return the context of all signals in the EditWindow given to the constructor.
* @return the context to prepend to all signals in the EditWindow.
* If the EditWindow is directly associated with a WaveformWindow, returns "".
*/
public VarContext getContext() { return context; }
}
// ************************************* HIGHLIGHT LISTENER FOR ALL WAVEFORM WINDOWS *************************************
private class WaveformWindowHighlightListener implements HighlightListener
{
/**
* Method to highlight waveform signals corresponding to circuit networks that are highlighted.
* Method is called when any edit window changes its highlighting.
*/
public void highlightChanged(Highlighter which)
{
// if this is a response to crossprobing from waveform to schematic, stop now
if (freezeWaveformHighlighting) return;
// find the EditWindow that this change comes from
WindowFrame highWF = which.getWindowFrame();
if (highWF == null) return;
if (!(highWF.getContent() instanceof EditWindow)) return;
EditWindow wnd = (EditWindow)highWF.getContent();
crossProbeEditWindowToWaveform(wnd, which);
// // loop through all windows, looking for waveform windows
// if (which == wnd.getHighlighter())
// {
// for(Iterator<WindowFrame> wIt = WindowFrame.getWindows(); wIt.hasNext(); )
// {
// WindowFrame wf = wIt.next();
// if (!(wf.getContent() instanceof WaveformWindow)) continue;
// WaveformWindow ww = (WaveformWindow)wf.getContent();
// ww.crossProbeEditWindowToWaveform(wnd, which);
// }
// }
}
/**
* 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) {}
}
// ************************************* HELPER CLASS FOR WAVEFORM WINDOW *************************************
private static class WaveComponentListener implements ComponentListener
{
private JPanel panel;
public WaveComponentListener(JPanel panel) { this.panel = panel; }
public void componentHidden(ComponentEvent e) {}
public void componentMoved(ComponentEvent e) {}
public void componentResized(ComponentEvent e)
{
panel.repaint();
}
public void componentShown(ComponentEvent e) {}
}
public void propertyChange(PropertyChangeEvent e) {}
/**
* Method to display simulation data in an existing waveform window; ignores preferences.
* @param sd the simulation data to display.
* @param ww the waveform window to load.
* If null, create a new waveform window.
*/
public static void refreshSimulationData(Stimuli sd, WaveformWindow ww) {
// if the window already exists, update the data
ww.setSimData(sd);
}
/**
* Method to display simulation data in a new waveform window.
* @param sd the simulation data to display.
* @param ww the waveform window to load.
* If null, create a new waveform window.
*/
public static void showSimulationDataInNewWindow(Stimuli sd) {
WaveformWindow ww = null;
Iterator<SignalCollection> scIt = sd.getSignalCollections();
if (!scIt.hasNext())
{
System.out.println("ERROR: No simulation data found: waveform window not shown");
return;
}
SignalCollection sc = scIt.next();
// create a waveform window
WindowFrame wf = WindowFrame.createWaveformWindow(sd);
ww = (WaveformWindow)wf.getContent();
// if the data has an associated cell, see if that cell remembers the signals that were in the waveform window
if (sd.getCell() != null)
{
String [] signalNames = WaveformWindow.getSignalOrder(sd.getCell());
boolean showedSomething = false;
boolean wantUnlockedTime = false;
for(int i=0; i<signalNames.length; i++)
{
String signalName = signalNames[i];
Signal<?> xAxisSignal = null;
int start = 0;
if (signalName.startsWith("\t"))
{
// has panel type and X axis information
int openPos = signalName.indexOf('(');
int tabPos = signalName.indexOf('\t', 1);
start = tabPos+1;
if (openPos >= 0) tabPos = openPos;
}
Panel wp = null;
boolean firstSignal = true;
// add signals to the panel
for(;;)
{
int tabPos = signalName.indexOf('\t', start);
String sigName = null;
if (tabPos < 0) sigName = signalName.substring(start); else
{
sigName = signalName.substring(start, tabPos);
start = tabPos+1;
}
Color sigColor = null;
int colorPos = sigName.indexOf(" {");
if (colorPos >= 0)
{
String [] colorNames = sigName.substring(colorPos+2).split(",");
int red = TextUtils.atoi(colorNames[0]);
int green = TextUtils.atoi(colorNames[1]);
int blue = TextUtils.atoi(colorNames[2]);
sigColor = new Color(red, green, blue);
sigName = sigName.substring(0, colorPos);
}
Signal<?> sSig = findSignalForNetwork(sc, sigName);
if (sSig != null)
{
if (firstSignal)
{
firstSignal = false;
int height = User.getWaveformDigitalPanelHeight();
if (sd.isAnalog()) height = User.getWaveformAnalogPanelHeight();
wp = new Panel(ww, height);
if (xAxisSignal != null)
wp.setXAxisSignal(xAxisSignal);
wp.makeSelectedPanel(-1, -1);
showedSomething = true;
}
WaveSignal ws = new WaveSignal(wp, sSig);
if (sigColor != null)
ws.setColor(sigColor);
}
if (tabPos < 0) break;
}
}
if (showedSomething)
{
if (wantUnlockedTime)
{
ww.togglePanelXAxisLock();
for(Iterator<Panel> it = ww.getPanels(); it.hasNext(); )
{
Panel panel = it.next();
panel.makeSelectedPanel(-1, -1);
ww.fillScreen();
}
} else
{
ww.fillScreen();
}
return;
}
}
if (sc == null) // wrong format?
{
System.out.println("ERROR: No simulation data found: waveform window not shown");
return;
}
// nothing saved, so show a default set of signals (if it even exists)
if (sd.isAnalog())
{
int height = User.getWaveformAnalogPanelHeight();
Panel wp = new Panel(ww, height);
wp.makeSelectedPanel(-1, -1);
} else
{
// put all top-level signals in, up to a limit
int numSignals = 0;
Iterable<Signal<?>> allSignals = sc.getSignals();
makeBussedSignals(sc, sd);
for(Signal<?> sig : allSignals) {
Signal<DigitalSample> sDSig = (Signal<DigitalSample>)sig;
if (sDSig.getSignalContext() != null) continue;
if (sDSig.getSignalName().indexOf('@') >= 0) continue;
int height = User.getWaveformDigitalPanelHeight();
Panel wp = new Panel(ww, height);
wp.makeSelectedPanel(-1, -1);
new WaveSignal(wp, sDSig);
numSignals++;
if (numSignals > 15) break;
}
}
ww.getPanel().validate();
ww.fillScreen();
}
private static void makeBussedSignals(SignalCollection sc, Stimuli sd)
{
Iterable<Signal<?>> signalsi = sc.getSignals();
ArrayList<Signal<?>> signals = new ArrayList<Signal<?>>();
for(Signal<?> s : signalsi) signals.add(s);
for(int i=0; i<signals.size(); i++)
{
Signal<?> sSig = signals.get(i);
int thisBracketPos = sSig.getSignalName().indexOf('[');
if (thisBracketPos < 0) continue;
String prefix = sSig.getSignalName().substring(0, thisBracketPos);
// see how many of the following signals are part of the bus
int j = i+1;
for( ; j<signals.size(); j++)
{
Signal<?> nextSig = signals.get(j);
// other signal must have the same root
int nextBracketPos = nextSig.getSignalName().indexOf('[');
if (nextBracketPos < 0) break;
if (thisBracketPos != nextBracketPos) break;
if (!prefix.equals(nextSig.getSignalName().substring(0, nextBracketPos))) break;
// other signal must have the same context
if (sSig.getSignalContext() == null ^ nextSig.getSignalContext() == null) break;
if (sSig.getSignalContext() != null)
{
if (!sSig.getSignalContext().equals(nextSig.getSignalContext())) break;
}
}
// see how many signals are part of the bus
int numSignals = j - i;
if (numSignals <= 1) continue;
// found a bus of signals: create the bus for it
DigitalSample.createSignal(sc, sd, prefix, sSig.getSignalContext());
i = j - 1;
}
}
/**
* Method to return the signal that corresponds to a given Network name.
* @param netName the Network name to find.
* @return the Signal that corresponds with the Network.
* Returns null if none can be found.
*/
public static Signal<?> findSignalForNetwork(SignalCollection sc, String netName) {
// look at all signal names in the cell
for(Signal<?> sSig : sc.getSignals()) {
String signalName = sSig.getFullName();
if (netName.equalsIgnoreCase(signalName)) return sSig;
// if the signal name has underscores, see if all alphabetic characters match
if (signalName.length() + 1 == netName.length() && netName.charAt(signalName.length()) == ']')
signalName += "_";
if (signalName.length() == netName.length() && signalName.indexOf('_') >= 0) {
boolean matches = true;
for(int i=0; i<signalName.length(); i++) {
char sigChar = signalName.charAt(i);
char netChar = netName.charAt(i);
if (TextUtils.isLetterOrDigit(sigChar) != TextUtils.isLetterOrDigit(netChar)) {
matches = false;
break;
}
if (TextUtils.isLetterOrDigit(sigChar) && TextUtils.canonicChar(sigChar) != TextUtils.canonicChar(netChar)) {
matches = false;
break;
}
}
if (matches) return sSig;
}
}
return null;
}
/**
* Get a list of signals that are from the same network.
* Extracted nets are the original name + delimiter + some junk
* @param ws the signal
* @return a list of signals
*/
public static List<Signal<?>> getSignalsFromExtractedNet(SignalCollection sc, Signal<?> ws) {
String sigName = ws.getFullName();
List<Signal<?>> ret = new ArrayList<Signal<?>>();
if (sigName == null) return ret;
sigName = TextUtils.canonicString(sigName);
sigName = ws.getBaseNameFromExtractedNet(sigName);
for(Signal<?> s : sc.getSignals())
if (ws.getBaseNameFromExtractedNet(TextUtils.canonicString(s.getFullName())).equals(sigName))
ret.add(s);
return ret;
}
}