/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: Panel.java * * Copyright (c) 2004 Sun Microsystems and Static Free Software * * Electric(tm) is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Electric(tm) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Electric(tm); see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, Mass 02111-1307, USA. */ package com.sun.electric.tool.user.waveform; import com.sun.electric.Main; import com.sun.electric.database.geometry.GenMath; import com.sun.electric.database.geometry.Poly; import com.sun.electric.database.geometry.PolyBase; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.technology.technologies.Artwork; import com.sun.electric.tool.Job; import com.sun.electric.tool.simulation.AnalogAnalysis; import com.sun.electric.tool.simulation.AnalogSignal; import com.sun.electric.tool.simulation.Analysis; import com.sun.electric.tool.simulation.BTreeNewSignal; import com.sun.electric.tool.simulation.DigitalAnalysis; import com.sun.electric.tool.simulation.DigitalSignal; import com.sun.electric.tool.simulation.NewSignal; import com.sun.electric.tool.simulation.ScalarSample; import com.sun.electric.tool.simulation.Signal; import com.sun.electric.tool.simulation.Simulation; import com.sun.electric.tool.simulation.Stimuli; import com.sun.electric.tool.simulation.Waveform; import com.sun.electric.tool.simulation.WaveformImpl; import com.sun.electric.tool.user.Highlight; import com.sun.electric.tool.user.Resources; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.dialogs.WaveformZoom; import com.sun.electric.tool.user.ui.ClickZoomWireListener; import com.sun.electric.tool.user.ui.ToolBar; import com.sun.electric.tool.user.ui.ZoomAndPanListener; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.dnd.DnDConstants; import java.awt.dnd.DragGestureEvent; import java.awt.dnd.DragGestureListener; import java.awt.dnd.DragSource; import java.awt.dnd.DragSourceDragEvent; import java.awt.dnd.DragSourceDropEvent; import java.awt.dnd.DragSourceEvent; import java.awt.dnd.DragSourceListener; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.font.GlyphVector; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.VolatileImage; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Set; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTable; import javax.swing.ScrollPaneConstants; import javax.swing.Scrollable; import javax.swing.SwingConstants; /** * This class defines a single panel of WaveSignals with an associated list of signal names. */ public class Panel extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener, KeyListener { /** Use VolatileImage for offscreen buffer */ private static final boolean USE_VOLATILE_IMAGE = true; /** Use anti-aliasing for lines */ private static final boolean USE_ANTIALIASING = false; /** the main waveform window this is part of */ private WaveformWindow waveWindow; /** the size of the window (in pixels) */ private Dimension sz; /** true if the size field is valid */ private boolean szValid; /** the signal on the X axis (null for time) */ private Signal xAxisSignal; /** maps signal buttons to the actual Signal */ private LinkedHashMap<JButton,WaveSignal> waveSignals = new LinkedHashMap<JButton,WaveSignal>(); /** the list of signal name buttons on the left */ private JPanel signalButtons; /** the JScrollPane with of signal name buttons */ private JScrollPane signalButtonsPane; /** the left side: with signal names etc. */ private JPanel leftHalf; /** the right side: with signal traces */ private JPanel rightHalf; /** the button to close this panel. */ private JButton close; /** the button to hide this panel. */ private JButton hide; /** the button to delete selected signal (analog). */ private JButton deleteSignal; /** the button to delete all signals (analog). */ private JButton deleteAllSignals; /** the signal name button (digital). */ private JButton digitalSignalButton; /** for selecting the type of data in this panel */ private JComboBox analysisCombo; /** displayed range along horozintal axis */ private double minXPosition, maxXPosition; /** low value displayed in this panel (analog) */ private double analogLowValue; /** high value displayed in this panel (analog) */ private double analogHighValue; /** vertical range displayed in this panel (analog) */ private double analogRange; /** true if an X axis cursor is being dragged */ private boolean draggingMain, draggingExt, draggingVertAxis; /** true if an area is being dragged */ private boolean draggingArea; /** list of measurements being displayed */ private List<Rectangle2D> measurementList; /** current measurement being displayed */ private Rectangle2D curMeasurement; /** true if this waveform panel is selected */ private boolean selected; /** true if this waveform panel is hidden */ private boolean hidden; /** the type of analysis shown in this panel */ private Analysis.AnalysisType analysisType; /** true for analog panel; false for digital */ private boolean analog; /** the horizontal ruler at the top of this panel. */ private HorizRuler horizRulerPanel; /** true if the horizontal ruler is logarithmic */ private boolean horizRulerPanelLogarithmic; /** true if this panel is logarithmic in Y */ private boolean vertPanelLogarithmic; /** the number of this panel. */ private int panelNumber; /** extent of area dragged-out by cursor */ private double dragStartXD, dragStartYD; /** extent of area dragged-out by cursor */ private double dragEndXD, dragEndYD; /** the location of the Y axis vertical line */ private int vertAxisPos; /** the smallest nonzero X value (for log drawing) */ private double smallestXValue; /** the smallest nonzero Y value (for log drawing) */ private double smallestYValue; /** the background color of a button */ private static Color background = null; /** The color of the grid (a gray) */ private static Color gridColor = new Color(0x808080); /** the panel numbering index */ private static int nextPanelNumber = 1; /** for determining double-clicks */ private static long lastClick = 0; /** current panel */ private static Panel curPanel; /** current X coordinate in the panel */ private static int curXPos; /** for drawing far-dotted lines */ private static final BasicStroke farDottedLine = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[] {4,12}, 0); /** the size of control point squares */ private static final int CONTROLPOINTSIZE = 6; /** the width of the panel label on the left */ private static final int VERTLABELWIDTH = 60; private static final ImageIcon iconHidePanel = Resources.getResource(WaveformWindow.class, "ButtonSimHide.gif"); private static final ImageIcon iconClosePanel = Resources.getResource(WaveformWindow.class, "ButtonSimClose.gif"); private static final ImageIcon iconDeleteSignal = Resources.getResource(WaveformWindow.class, "ButtonSimDelete.gif"); private static final ImageIcon iconDeleteAllSignals = Resources.getResource(WaveformWindow.class, "ButtonSimDeleteAll.gif"); private static final Cursor dragXPositionCursor = ToolBar.readCursor("CursorDragTime.gif", 8, 8); /** * Constructor creates a panel in a WaveformWindow. * @param waveWindow the WaveformWindow in which to place this Panel. * @param analysisType the type of data shown in this Panel. */ public Panel(WaveformWindow waveWindow, boolean analog, Analysis.AnalysisType analysisType) { // remember state this.waveWindow = waveWindow; setAnalysisType(analysisType); this.analog = analog; selected = false; panelNumber = nextPanelNumber++; vertAxisPos = VERTLABELWIDTH; horizRulerPanelLogarithmic = false; vertPanelLogarithmic = false; xAxisSignal = null; measurementList = new ArrayList<Rectangle2D>(); curMeasurement = null; // setup this panel window int height = User.getWaveformDigitalPanelHeight(); if (analog) height = User.getWaveformAnalogPanelHeight(); sz = new Dimension(50, height); szValid = false; setSize(sz.width, sz.height); setPreferredSize(sz); setLayout(new FlowLayout()); // add listeners --> BE SURE to remove listeners in finished() addKeyListener(this); addMouseListener(this); addMouseMotionListener(this); addMouseWheelListener(this); setXAxisRange(waveWindow.getLowDefaultHorizontalRange(), waveWindow.getHighDefaultHorizontalRange()); // the left side with signal names leftHalf = new WaveformWindow.OnePanel(this, waveWindow); leftHalf.setLayout(new GridBagLayout()); leftHalf.setPreferredSize(new Dimension(100, height)); // a drop target for the signal panel new DropTarget(leftHalf, DnDConstants.ACTION_LINK, WaveformWindow.waveformDropTarget, true); // a separator at the top JSeparator sep = new JSeparator(SwingConstants.HORIZONTAL); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 5; gbc.gridheight = 1; gbc.weightx = 1; gbc.weighty = 0; gbc.anchor = GridBagConstraints.NORTH; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(4, 0, 4, 0); leftHalf.add(sep, gbc); // the name of this panel if (analog) { // analog panel JLabel label = new DragLabel(Integer.toString(panelNumber)); label.setToolTipText("Identification number of this waveform panel (drag the number to rearrange panels)"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0.2; gbc.weighty = 0; gbc.anchor = GridBagConstraints.NORTHWEST; gbc.fill = GridBagConstraints.NONE; gbc.insets = new Insets(4, 4, 4, 4); leftHalf.add(label, gbc); } else { // digital panel digitalSignalButton = new DragButton(Integer.toString(panelNumber), panelNumber); digitalSignalButton.setBorderPainted(false); digitalSignalButton.setForeground(Color.BLACK); digitalSignalButton.setToolTipText("Identification number of this waveform panel (drag the number to rearrange panels)"); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 1; gbc.weighty = 1; gbc.anchor = GridBagConstraints.WEST; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(0, 4, 0, 4); leftHalf.add(digitalSignalButton, gbc); digitalSignalButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { digitalSignalNameClicked(evt); } }); } // the close button for this panel close = new JButton(iconClosePanel); close.setBorderPainted(false); close.setDefaultCapable(false); close.setToolTipText("Close this waveform panel"); Dimension minWid = new Dimension(iconClosePanel.getIconWidth()+4, iconClosePanel.getIconHeight()+4); close.setMinimumSize(minWid); close.setPreferredSize(minWid); gbc = new GridBagConstraints(); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 0.2; gbc.weighty = 0; if (analog) gbc.anchor = GridBagConstraints.NORTH; else gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.NONE; leftHalf.add(close, gbc); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { closePanel(); } }); // the hide button for this panel hide = new JButton(iconHidePanel); hide.setBorderPainted(false); hide.setDefaultCapable(false); hide.setToolTipText("Hide this waveform panel"); minWid = new Dimension(iconHidePanel.getIconWidth()+4, iconHidePanel.getIconHeight()+4); hide.setMinimumSize(minWid); hide.setPreferredSize(minWid); gbc = new GridBagConstraints(); gbc.gridx = 2; gbc.gridy = 1; gbc.weightx = 0.2; gbc.weighty = 0; if (analog) gbc.anchor = GridBagConstraints.NORTH; else gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.NONE; leftHalf.add(hide, gbc); hide.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { hidePanel(); } }); if (analog) { // the "delete signal" button for this panel (analog only) deleteSignal = new JButton(iconDeleteSignal); deleteSignal.setBorderPainted(false); deleteSignal.setDefaultCapable(false); deleteSignal.setToolTipText("Remove selected signals from this panel"); minWid = new Dimension(iconDeleteSignal.getIconWidth()+4, iconDeleteSignal.getIconHeight()+4); deleteSignal.setMinimumSize(minWid); deleteSignal.setPreferredSize(minWid); gbc = new GridBagConstraints(); gbc.gridx = 3; gbc.gridy = 1; gbc.weightx = 0.2; gbc.weighty = 0; gbc.anchor = GridBagConstraints.NORTH; gbc.fill = GridBagConstraints.NONE; leftHalf.add(deleteSignal, gbc); deleteSignal.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { deleteSignalFromPanel(); } }); // the "delete all signal" button for this panel (analog only) deleteAllSignals = new JButton(iconDeleteAllSignals); deleteAllSignals.setBorderPainted(false); deleteAllSignals.setDefaultCapable(false); deleteAllSignals.setToolTipText("Remove all signals from this panel"); minWid = new Dimension(iconDeleteAllSignals.getIconWidth()+4, iconDeleteAllSignals.getIconHeight()+4); deleteAllSignals.setMinimumSize(minWid); deleteAllSignals.setPreferredSize(minWid); gbc = new GridBagConstraints(); gbc.gridx = 4; gbc.gridy = 1; gbc.weightx = 0.2; gbc.weighty = 0; gbc.anchor = GridBagConstraints.NORTH; gbc.fill = GridBagConstraints.NONE; leftHalf.add(deleteAllSignals, gbc); deleteAllSignals.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { deleteAllSignalsFromPanel(); } }); // the "signal type" selector for this panel (analog only) boolean hasACData = waveWindow.getSimData().findAnalysis(Analysis.ANALYSIS_AC) != null; boolean hasDCData = waveWindow.getSimData().findAnalysis(Analysis.ANALYSIS_DC) != null; boolean hasMeasData = waveWindow.getSimData().findAnalysis(Analysis.ANALYSIS_MEAS) != null; if (hasACData || hasDCData || hasMeasData) { analysisCombo = new JComboBox(); analysisCombo.addItem(Analysis.ANALYSIS_TRANS.toString()); if (hasACData) analysisCombo.addItem(Analysis.ANALYSIS_AC.toString()); if (hasDCData) analysisCombo.addItem(Analysis.ANALYSIS_DC.toString()); if (hasMeasData) analysisCombo.addItem(Analysis.ANALYSIS_MEAS.toString()); analysisCombo.setToolTipText("Sets the type of data seen in this panel"); analysisCombo.setSelectedItem(analysisType.toString()); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 5; gbc.gridheight = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.HORIZONTAL; leftHalf.add(analysisCombo, gbc); analysisCombo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { setPanelSignalType(); } }); } // the list of signals in this panel (analog only) signalButtons = new JPanelX(); signalButtons.setLayout(new BoxLayout(signalButtons, BoxLayout.Y_AXIS)); signalButtonsPane = new JScrollPane(signalButtons); signalButtonsPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); signalButtons.setAlignmentX(1.0f); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 5; gbc.gridheight = 1; gbc.weightx = 1; gbc.weighty = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.BOTH; leftHalf.add(signalButtonsPane, gbc); } // the right side with signal traces rightHalf = new JPanel(); rightHalf.setLayout(new GridBagLayout()); rightHalf.setPreferredSize(new Dimension(100, height)); // a drop target for the signal panel new DropTarget(this, DnDConstants.ACTION_LINK, WaveformWindow.waveformDropTarget, true); // a separator at the top sep = new JSeparator(SwingConstants.HORIZONTAL); gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 1; gbc.weighty = 0; gbc.anchor = GridBagConstraints.NORTH; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(4, 0, 4, 0); rightHalf.add(sep, gbc); // the horizontal ruler (if separate rulers in each panel) if (!waveWindow.isXAxisLocked()) addHorizRulerPanel(); // the waveform display for this panel gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 1; gbc.weighty = 1; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(0, 0, 0, 0); rightHalf.add(this, gbc); // add to list of wave panels waveWindow.addPanel(this); // put the left and right sides into the window if (WaveformWindow.USETABLES) { waveWindow.getWaveformTable().repaint(); waveWindow.getWaveformTable().doLayout(); waveWindow.getWaveformTable().updateUI(); } else { waveWindow.getSignalNamesPanel().add(leftHalf); waveWindow.getSignalTracesPanel().add(rightHalf); } // rebuild list of panels waveWindow.rebuildPanelList(); waveWindow.redrawAllPanels(); } // ************************************* MISCELLANEOUS ************************************* /** A subclass of JPanel which implements Scrollable and always tracks its JScrollPane's height */ private static class JPanelX extends JPanel implements Scrollable { public JPanelX() { } public boolean getScrollableTracksViewportWidth() { return true; } public boolean getScrollableTracksViewportHeight() { return false; } public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 1; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 1; } } public WaveformWindow getWaveWindow() { return waveWindow; } /** * Method to get rid of this Panel. */ public void finished() { // remove myself from listener list removeKeyListener(this); removeMouseListener(this); removeMouseMotionListener(this); removeMouseWheelListener(this); } public JPanel getLeftHalf() { return leftHalf; } public JPanel getRightHalf() { return rightHalf; } /** * Make this panel show a linear Y axis. */ private void makeLinear() { setPanelLogarithmicVertically(false); } /** * Make this panel show a logarithmic Y axis. */ private void makeLogarithmic() { setPanelLogarithmicVertically(true); } public void setPanelLogarithmicVertically(boolean logarithmic) { vertPanelLogarithmic = logarithmic; repaintContents(); } public boolean isAnalog() { return analog; } public Analysis.AnalysisType getAnalysisType() { return analysisType; }; public void setAnalysisType(Analysis.AnalysisType a) { analysisType = a; computeSmallestValues(); } /** * Method to ensure that a signal can be shown in this panel. * Displays an error if not. * @param sSig the signal being tested. * @return true if the signal is wrong and cannot appear in this panel. */ public boolean wrongPanelType(Signal sSig) { if (sSig.getAnalysis().getAnalysisType() == getAnalysisType()) return false; JOptionPane.showMessageDialog(Main.getCurrentJFrame(), "Cannot drop a " + sSig.getAnalysis().getAnalysisType() + " signal onto a " + getAnalysisType() + " panel. " + "First convert the panel with the popup in the upper-left.", "Error Displaying Signals", JOptionPane.ERROR_MESSAGE); return true; } public JPanel getSignalButtons() { return signalButtons; }; public JScrollPane getSignalButtonsPane() { return signalButtonsPane; }; public JButton getDigitalSignalButton() { return digitalSignalButton; } public int getPanelNumber() { return panelNumber; } public void setPanelLogarithmicHorizontally(boolean logarithmic) { horizRulerPanelLogarithmic = logarithmic; horizRulerPanel.repaint(); } public boolean isPanelLogarithmicHorizontally() { if (waveWindow.isXAxisLocked()) return waveWindow.isWaveWindowLogarithmic(); return horizRulerPanelLogarithmic; } public boolean isPanelLogarithmicVertically() { return vertPanelLogarithmic; } public int getVertAxisPos() { return vertAxisPos; } public void setVertAxisPos(int x) { vertAxisPos = x; } public static Panel getCurrentPanel() { return curPanel; } public static int getCurrentXPos() { return curXPos; } public Dimension getSz() { return sz; } // ************************************* SIGNALS IN THE PANEL ************************************* public void addSignal(WaveSignal sig, JButton but) { waveSignals.put(but, sig); } public void removeSignal(JButton but) { if (signalButtons != null) signalButtons.remove(but); waveSignals.remove(but); } public void removeAllSignals() { waveSignals.clear(); } /** * Method to return a List of WaveSignals in this panel. * @return a List of WaveSignals in this panel. */ public List<WaveSignal> getSignals() { List<WaveSignal> signals = new ArrayList<WaveSignal>(); for(JButton but : waveSignals.keySet()) { WaveSignal ws = waveSignals.get(but); signals.add(ws); } return signals; } public int getNumSignals() { return waveSignals.size(); } public WaveSignal findWaveSignal(Signal sig) { for(JButton but : waveSignals.keySet()) { WaveSignal ws = waveSignals.get(but); if (ws.getSignal() == sig) return ws; } return null; } public WaveSignal findWaveSignal(JButton but) { WaveSignal sig = waveSignals.get(but); return sig; } public JButton findButton(WaveSignal ws) { for(JButton but : waveSignals.keySet()) { WaveSignal oWs = waveSignals.get(but); if (oWs == ws) return but; } return null; } private void deleteSignalFromPanel() { waveWindow.deleteSignalFromPanel(this); } private void deleteAllSignalsFromPanel() { waveWindow.deleteAllSignalsFromPanel(this); } private void setPanelSignalType() { String typeName = (String)analysisCombo.getSelectedItem(); Analysis.AnalysisType analysisType = Analysis.AnalysisType.findAnalysisType(typeName); if (getAnalysisType() != analysisType && getNumSignals() > 0) { String warning = "The signals in this panel are not " + analysisType + " data. Remove them from the panel?"; int response = JOptionPane.showConfirmDialog(Main.getCurrentJFrame(), warning); if (response != JOptionPane.YES_OPTION) { // aborted: reset panel type analysisCombo.setSelectedItem(getAnalysisType().toString()); return; } xAxisSignal = null; if (waveWindow.isXAxisLocked()) waveWindow.setXAxisSignalAll(null); waveWindow.deleteAllSignalsFromPanel(this); } setAnalysisType(analysisType); // redo X scale if time unlocked or this is the only panel Analysis an = waveWindow.getSimData().findAnalysis(analysisType); if (an != null) { Rectangle2D bounds = an.getBounds(); double lowValue = bounds.getMinY(); double highValue = bounds.getMaxY(); setYAxisRange(lowValue, highValue); if (!waveWindow.isXAxisLocked() || waveWindow.getNumPanels() == 1) { // set the X range setXAxisRange(an.getLeftEdge(), an.getRightEdge()); } } repaintWithRulers(); } // ************************************* THE HORIZONTAL RULER ************************************* public void addHorizRulerPanel() { horizRulerPanel = new HorizRuler(this, waveWindow); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.gridheight = 1; gbc.weightx = 1; gbc.weighty = 0; gbc.anchor = GridBagConstraints.CENTER; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(0, 0, 0, 0); rightHalf.add(horizRulerPanel, gbc); } public void removeHorizRulerPanel() { rightHalf.remove(horizRulerPanel); horizRulerPanel = null; } public HorizRuler getHorizRuler() { return horizRulerPanel; } public Signal getXAxisSignal() { return xAxisSignal; } public void setXAxisSignal(Signal sig) { xAxisSignal = sig; } // ************************************* PANEL DISPLAY CONTROL ************************************* public void hidePanel() { waveWindow.hidePanel(this); } public void closePanel() { waveWindow.closePanel(this); waveWindow.saveSignalOrder(); } private void toggleBusContents() { // this panel must have one signal Collection<WaveSignal> theSignals = waveSignals.values(); if (theSignals.size() != 1) return; // the only signal must be digital WaveSignal ws = theSignals.iterator().next(); if (!(ws.getSignal() instanceof DigitalSignal)) return; // the digital signal must be a bus DigitalSignal sDSig = (DigitalSignal)ws.getSignal(); List<DigitalSignal> bussedSignals = sDSig.getBussedSignals(); if (bussedSignals == null) return; // see if any of the bussed signals are displayed boolean opened = false; for(Signal subSig : bussedSignals) { DigitalSignal subDS = (DigitalSignal)subSig; WaveSignal subWs = waveWindow.findDisplayedSignal(subDS); if (subWs != null) { opened = true; break; } } // now open or close the bus if (opened) { // opened: remove all entries on the bus List<Panel> allPanels = new ArrayList<Panel>(); for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) allPanels.add(it.next()); for(Signal subSig : bussedSignals) { DigitalSignal subDS = (DigitalSignal)subSig; WaveSignal subWs = waveWindow.findDisplayedSignal(subDS); if (subWs != null) { Panel wp = subWs.getPanel(); waveWindow.closePanel(wp); allPanels.remove(wp); } } } else { // closed: add all entries on the bus int increment = 1; if (WaveformWindow.USETABLES) { waveWindow.stopEditing(); } for(Signal subSig : bussedSignals) { DigitalSignal subDS = (DigitalSignal)subSig; Panel wp = waveWindow.makeNewPanel(null); WaveSignal wsig = new WaveSignal(wp, subDS); if (WaveformWindow.USETABLES) { // remove the panels and put them in the right place waveWindow.removePanel(wsig.getPanel()); int destIndex = waveWindow.getPanelIndex(this); waveWindow.addPanel(wsig.getPanel(), destIndex+increment); } else { // remove the panels and put them in the right place waveWindow.getSignalNamesPanel().remove(wsig.getPanel().leftHalf); waveWindow.getSignalTracesPanel().remove(wsig.getPanel().rightHalf); Component [] lefts = waveWindow.getSignalNamesPanel().getComponents(); int destIndex = 0; for( ; destIndex < lefts.length; destIndex++) { if (lefts[destIndex] == leftHalf) break; } waveWindow.getSignalNamesPanel().add(wsig.getPanel().leftHalf, destIndex+increment); waveWindow.getSignalTracesPanel().add(wsig.getPanel().rightHalf, destIndex+increment); } increment++; } if (WaveformWindow.USETABLES) { waveWindow.reloadTable(); } } waveWindow.validatePanel(); waveWindow.saveSignalOrder(); } // ************************************* X AND Y AXIS CONTROL ************************************* /** * Method to compute the smallest X and Y values (for log display). */ private void computeSmallestValues() { if (analysisType == null) return; Stimuli sd = waveWindow.getSimData(); Analysis an = sd.findAnalysis(analysisType); if (an == null) return; Rectangle2D anBounds = an.getBounds(); if (anBounds == null) return; // no signals smallestXValue = anBounds.getMinX(); smallestYValue = anBounds.getMinY(); // smallestXValue = anBounds.getWidth() / 1000; // smallestYValue = anBounds.getHeight() / 1000; } /** * Method to set the X axis range in this panel. * Since the panel may go backwards in time, these values aren't * guaranteed to run from left to right. * @param leftEdge the X axis value on the left side of the panel. * @param rightEdge the X axis value on the right side of the panel. */ public void setXAxisRange(double leftEdge, double rightEdge) { this.minXPosition = leftEdge; this.maxXPosition = rightEdge; } /** * Method to return the low X axis value shown in this panel. * @return the low X axis value shown in this panel. */ public double getMinXAxis() { return minXPosition; } /** * Method to return the high X axis value shown in this panel. * @return the high X axis value shown in this panel. */ public double getMaxXAxis() { return maxXPosition; } /** * Method to make this Panel show a signal fully. * @param sSig the signal to show (must be analog) */ public void fitToSignal(Signal sSig) { if (sSig instanceof AnalogSignal) { AnalogSignal as = (AnalogSignal)sSig; Rectangle2D rangeBounds = as.getBounds(); double lowValue = rangeBounds.getMinY(); double highValue = rangeBounds.getMaxY(); double range = highValue - lowValue; if (range == 0) range = 2; double rangeExtra = range / 10; setYAxisRange(lowValue - rangeExtra, highValue + rangeExtra); } } /** * Method to set the Y axis range in this panel. * @param low the low Y axis value. * @param high the high Y axis value. */ public void setYAxisRange(double low, double high) { if (low == high) { low -= 0.5; high += 0.5; } analogLowValue = low; analogHighValue = high; analogRange = analogHighValue - analogLowValue; } public double getYAxisRange() { return analogRange; } public double getYAxisLowValue() { return analogLowValue; } public double getYAxisHighValue() { return analogHighValue; } /** * Method to scale a simulation X value to the X coordinate in this window. * @param value the simulation X value. * @return the X coordinate of that simulation value on the screen. */ public int convertXDataToScreen(double value) { // see if doing logarithmic axes boolean log = waveWindow.isWaveWindowLogarithmic(); if (!waveWindow.isXAxisLocked()) log = horizRulerPanelLogarithmic; if (log) { // logarithmic axes if (value <= smallestXValue) value = smallestXValue; double logValue = Math.log10(value); double winMinX = minXPosition; if (winMinX <= 0) winMinX = smallestXValue; double logWinMinX = Math.log10(winMinX); double winMaxX = maxXPosition; if (winMaxX <= 0) winMaxX = smallestXValue; double logWinMaxX = Math.log10(winMaxX); double x = (logValue - logWinMinX) / (logWinMaxX - logWinMinX) * (sz.width - vertAxisPos) + vertAxisPos; return (int)x; } // linear axes double x = (value - minXPosition) / (maxXPosition - minXPosition) * (sz.width - vertAxisPos) + vertAxisPos; return (int)x; } /** * Method to scale an X coordinate from screen space to data space. * @param x the X coordinate on the screen. * @return the X value in the simulation corresponding to that screen coordinate. */ public double convertXScreenToData(int x) { // see if doing logarithmic axes boolean log = waveWindow.isWaveWindowLogarithmic(); if (!waveWindow.isXAxisLocked()) log = horizRulerPanelLogarithmic; if (log) { // logarithmic axes double winMinX = minXPosition; if (winMinX <= 0) winMinX = smallestXValue; double logWinMinX = Math.log10(winMinX); double winMaxX = maxXPosition; if (winMaxX <= 0) winMaxX = smallestXValue; double logWinMaxX = Math.log10(winMaxX); double xValue = Math.pow(10, ((double)(x - vertAxisPos)) / (sz.width - vertAxisPos) * (logWinMaxX - logWinMinX) + logWinMinX); return xValue; } // linear axes double xValue = ((double)(x - vertAxisPos)) / (sz.width - vertAxisPos) * (maxXPosition - minXPosition) + minXPosition; return xValue; } /** * Method to scale a simulation Y value to the Y coordinate in this window. * @param value the simulation Y value. * @return the Y coordinate of that simulation value on the screen */ private int convertYDataToScreen(double value) { if (vertPanelLogarithmic) { // logarithmic axes if (value <= smallestYValue) value = smallestYValue; double logValue = Math.log10(value); double winMinY = analogLowValue; if (winMinY <= 0) winMinY = smallestYValue; double logWinMinY = Math.log10(winMinY); double winMaxY = analogHighValue; if (winMaxY <= 0) winMaxY = smallestYValue; double logWinMaxY = Math.log10(winMaxY); double y = sz.height - 1 - (logValue - logWinMinY) / (logWinMaxY - logWinMinY) * (sz.height-1); return (int)y; } // linear axes double y = sz.height - 1 - (value - analogLowValue) / analogRange * (sz.height-1); return (int)y; } /** * Method to scale a Y coordinate from screen space to data space. * @param y the Y coordinate on the screen. * @return the Y value in the simulation corresponding to that screen coordinate. */ private double convertYScreenToData(int y) { if (vertPanelLogarithmic) { // logarithmic axes double winMinY = analogLowValue; if (winMinY <= 0) winMinY = smallestYValue; double logWinMinY = Math.log10(winMinY); double winMaxY = analogHighValue; if (winMaxY <= 0) winMaxY = smallestYValue; double logWinMaxY = Math.log10(winMaxY); double yValue = Math.pow(10, logWinMinY - (y - sz.height + 1) * (logWinMaxY - logWinMinY) / (sz.height-1)); return yValue; } // linear axes double value = 0; if (sz.height > 1) value = analogLowValue - (y - sz.height + 1) * analogRange / (sz.height-1); return value; } // ************************************* DISPLAY CONTROL ************************************* /** * Method to repaint this window and its associated ruler panel. */ public void repaintWithRulers() { if (horizRulerPanel != null) horizRulerPanel.repaint(); else { waveWindow.getMainHorizRuler().repaint(); } repaintContents(); } /** * Method to repaint the panel. * Rebuilds the offscreen image and schedules a repaint. */ public void repaintContents() { needRepaintOffscreenImage = true; // repaintOffscreenImage(); if (WaveformWindow.USETABLES) { waveWindow.getWaveformTable().repaint(); } else { repaint(); } } private boolean needRepaintOffscreenImage; private Image offscreen; /** * Method to repaint this Panel. */ public void paint(Graphics g) { // requestFocus moved to mousePressed(). // to enable keys to be received //if (waveWindow.getWindowFrame() == WindowFrame.getCurrentWindowFrame()) // requestFocus(); sz = getSize(); szValid = true; int wid = sz.width; int hei = sz.height; // long startTime = System.currentTimeMillis(); // long repaintOffscreenTime = startTime; if (USE_VOLATILE_IMAGE) { VolatileImage offscreen = (VolatileImage)this.offscreen; do { int returnCode = VolatileImage.IMAGE_INCOMPATIBLE; if (offscreen != null && offscreen.getWidth() == wid && offscreen.getHeight() == hei) returnCode = offscreen.validate(getGraphicsConfiguration()); if (returnCode == VolatileImage.IMAGE_INCOMPATIBLE) { // old offscreen doesn't work with new GraphicsConfig; re-create it if (offscreen != null) offscreen.flush(); this.offscreen = offscreen = createVolatileImage(wid, hei); needRepaintOffscreenImage = true; } if (returnCode != VolatileImage.IMAGE_OK || needRepaintOffscreenImage) { // Contents need to be restored repaintOffscreenImage(wid, hei); } if (offscreen.contentsLost()) continue; // repaintOffscreenTime = System.currentTimeMillis(); g.drawImage(offscreen, 0, 0, null); } while (offscreen.contentsLost()); } else { BufferedImage offscreen = (BufferedImage)this.offscreen; if (offscreen == null || offscreen.getWidth() != wid || offscreen.getHeight() != hei) { this.offscreen = offscreen = new BufferedImage(wid, hei, BufferedImage.TYPE_INT_RGB); needRepaintOffscreenImage = true; } if (needRepaintOffscreenImage) { repaintOffscreenImage(wid, hei); } // repaintOffscreenTime = System.currentTimeMillis(); g.drawImage(offscreen, 0, 0, null); } // long drawImageTime = System.currentTimeMillis(); if (WaveformWindow.USETABLES) { Dimension tableSz = waveWindow.getWaveformTable().getSize(); Point screenLoc = waveWindow.getWaveformTable().getLocationOnScreen(); waveWindow.setScreenXSize(screenLoc.x + tableSz.width - wid, screenLoc.x + tableSz.width); } else { Point screenLoc = getLocationOnScreen(); waveWindow.setScreenXSize(screenLoc.x, screenLoc.x + wid); } paintDragging((Graphics2D)g, wid, hei); // long dragTime = System.currentTimeMillis(); // System.out.println("Panel" + panelNumber + // " offscreen " + (repaintOffscreenTime - startTime) + " msec;" + // " drawImage " + (drawImageTime - repaintOffscreenTime) + " msec;" + // " dragging " + (dragTime - drawImageTime) + " msec"); } private void repaintOffscreenImage(int wid, int hei) { needRepaintOffscreenImage = false; Graphics2D offscreenGraphics = (Graphics2D)offscreen.getGraphics(); // clear the buffer offscreenGraphics.setColor(new Color(User.getColor(User.ColorPrefType.WAVE_BACKGROUND))); offscreenGraphics.fillRect(0, 0, wid, hei); drawPanelContents(wid, hei, offscreenGraphics, null, null); offscreenGraphics.dispose(); } private void paintDragging(Graphics2D g, int wid, int hei) { g.setColor(new Color(User.getColor(User.ColorPrefType.WAVE_FOREGROUND))); // draw the X position cursors g.setStroke(Highlight.dashedLine); int x = convertXDataToScreen(waveWindow.getMainXPositionCursor()); if (x >= vertAxisPos) g.drawLine(x, 0, x, hei); g.setStroke(farDottedLine); x = convertXDataToScreen(waveWindow.getExtensionXPositionCursor()); if (x >= vertAxisPos) g.drawLine(x, 0, x, hei); g.setStroke(Highlight.solidLine); // show dragged area if there if (draggingArea) { int lowX = Math.min(convertXDataToScreen(dragStartXD), convertXDataToScreen(dragEndXD)); int highX = Math.max(convertXDataToScreen(dragStartXD), convertXDataToScreen(dragEndXD)); int lowY = Math.min(convertYDataToScreen(dragStartYD), convertYDataToScreen(dragEndYD)); int highY = Math.max(convertYDataToScreen(dragStartYD), convertYDataToScreen(dragEndYD)); g.drawLine(lowX, lowY, lowX, highY); g.drawLine(lowX, highY, highX, highY); g.drawLine(highX, highY, highX, lowY); g.drawLine(highX, lowY, lowX, lowY); } for(Rectangle2D meas : measurementList) { int lowX = Math.min(convertXDataToScreen(meas.getMinX()), convertXDataToScreen(meas.getMaxX())); int highX = Math.max(convertXDataToScreen(meas.getMinX()), convertXDataToScreen(meas.getMaxX())); int lowY = Math.min(convertYDataToScreen(meas.getMinY()), convertYDataToScreen(meas.getMaxY())); int highY = Math.max(convertYDataToScreen(meas.getMinY()), convertYDataToScreen(meas.getMaxY())); g.drawLine(lowX, lowY, lowX, highY); g.drawLine(lowX, highY, highX, highY); g.drawLine(highX, highY, highX, lowY); g.drawLine(highX, lowY, lowX, lowY); // show dimensions while dragging double lowXValue = convertXScreenToData(lowX); double highXValue = convertXScreenToData(highX); double lowValue = convertYScreenToData(highY); double highValue = convertYScreenToData(lowY); g.setFont(waveWindow.getFont()); // show the low X value and arrow String lowXValueString = TextUtils.convertToEngineeringNotation(lowXValue, "s"); GlyphVector gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), lowXValueString); Rectangle2D glyphBounds = gv.getLogicalBounds(); int textWid = (int)glyphBounds.getWidth(); int textHei = (int)glyphBounds.getHeight(); int textY = (lowY+highY)/2; g.drawString(lowXValueString, lowX-textWid-6, textY+textHei/2-10); g.drawLine(lowX-1, textY, lowX-textWid, textY); g.drawLine(lowX-1, textY, lowX-6, textY+4); g.drawLine(lowX-1, textY, lowX-6, textY-4); // show the high X value and arrow String highXValueString = TextUtils.convertToEngineeringNotation(highXValue, "s"); gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), highXValueString); glyphBounds = gv.getLogicalBounds(); textWid = (int)glyphBounds.getWidth(); textHei = (int)glyphBounds.getHeight(); int highXValueTextWid = textWid; g.drawString(highXValueString, highX+6, textY+textHei/2-10); g.drawLine(highX+1, textY, highX+textWid, textY); g.drawLine(highX+1, textY, highX+6, textY+4); g.drawLine(highX+1, textY, highX+6, textY-4); // show the difference X value String xDiffString = TextUtils.convertToEngineeringNotation(highXValue-lowXValue, "s"); gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), xDiffString); glyphBounds = gv.getLogicalBounds(); textWid = (int)glyphBounds.getWidth(); textHei = (int)glyphBounds.getHeight(); if (textWid + 24 < highX - lowX) { // fits inside: draw arrows around text int yPosText = highY + textHei*5; int yPos = yPosText - textHei/2; int xCtr = (highX+lowX)/2; g.drawString(xDiffString, xCtr - textWid/2, yPosText); g.drawLine(lowX, yPos, xCtr - textWid/2 - 2, yPos); g.drawLine(highX, yPos, xCtr + textWid/2 + 2, yPos); g.drawLine(lowX, yPos, lowX+5, yPos+4); g.drawLine(lowX, yPos, lowX+5, yPos-4); g.drawLine(highX, yPos, highX-5, yPos+4); g.drawLine(highX, yPos, highX-5, yPos-4); } else { // does not fit inside: draw outside of arrows int yPosText = highY + textHei*5; int yPos = yPosText - textHei/2; g.drawString(xDiffString, highX + 12, yPosText); g.drawLine(lowX, yPos, lowX-10, yPos); g.drawLine(highX, yPos, highX+10, yPos); g.drawLine(lowX, yPos, lowX-5, yPos+4); g.drawLine(lowX, yPos, lowX-5, yPos-4); g.drawLine(highX, yPos, highX+5, yPos+4); g.drawLine(highX, yPos, highX+5, yPos-4); } if (analog) { // show the low value String lowValueString = TextUtils.convertToEngineeringNotation(highValue, null); gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), lowValueString); glyphBounds = gv.getLogicalBounds(); textWid = (int)glyphBounds.getWidth(); textHei = (int)glyphBounds.getHeight(); int xP = (lowX+highX)/2; int yText = lowY - 10 - textHei; g.drawString(lowValueString, xP, yText - 2); g.drawLine(xP, lowY-1, xP, yText); g.drawLine(xP, lowY-1, xP+4, lowY-5); g.drawLine(xP, lowY-1, xP-4, lowY-5); // show the high value String highValueString = TextUtils.convertToEngineeringNotation(lowValue, null); gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), highValueString); glyphBounds = gv.getLogicalBounds(); textWid = (int)glyphBounds.getWidth(); textHei = (int)glyphBounds.getHeight(); yText = highY + 10 + textHei; g.drawString(highValueString, xP, yText + textHei + 2); g.drawLine(xP, highY+1, xP, yText); g.drawLine(xP, highY+1, xP+4, highY+5); g.drawLine(xP, highY+1, xP-4, highY+5); // show the value difference String valueDiffString = TextUtils.convertToEngineeringNotation(highValue - lowValue, null); gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), valueDiffString); glyphBounds = gv.getLogicalBounds(); textWid = (int)glyphBounds.getWidth(); textHei = (int)glyphBounds.getHeight(); if (textHei + 12 < highY - lowY) { // fits inside: draw arrows around text int xPos = highX + highXValueTextWid + 30; int yCtr = (highY+lowY)/2; g.drawString(valueDiffString, xPos+2, yCtr + textHei/2); g.drawLine(xPos, lowY, xPos, highY); g.drawLine(xPos, lowY, xPos+4, lowY+5); g.drawLine(xPos, lowY, xPos-4, lowY+5); g.drawLine(xPos, highY, xPos+4, highY-5); g.drawLine(xPos, highY, xPos-4, highY-5); } else { // does not fit inside: draw outside of arrows int xPos = highX + highXValueTextWid + 30; g.drawString(valueDiffString, xPos+4, lowY - textHei/2 - 4); g.drawLine(xPos, lowY, xPos, lowY-10); g.drawLine(xPos, highY, xPos, highY+10); g.drawLine(xPos, lowY, xPos+4, lowY-5); g.drawLine(xPos, lowY, xPos-4, lowY-5); g.drawLine(xPos, highY, xPos+4, highY+5); g.drawLine(xPos, highY, xPos-4, highY+5); } } } } private void drawPanelContents(int wid, int hei, Graphics2D localGraphics, Rectangle2D bounds, List<PolyBase> polys) { // draw the grid first (behind the signals) if (analog && waveWindow.isShowGrid()) { if (localGraphics != null) { localGraphics.setStroke(Highlight.dottedLine); localGraphics.setColor(gridColor); } // draw the vertical grid lines double displayedXLow = convertXScreenToData(vertAxisPos); double displayedXHigh = convertXScreenToData(wid); StepSize ss = new StepSize(displayedXHigh, displayedXLow, 10); if (ss.getSeparation() != 0.0) { double value = ss.getLowValue(); for(;;) { if (value >= displayedXLow) { if (value > ss.getHighValue()) break; int x = convertXDataToScreen(value); if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(x, 0), new Point2D.Double(x, hei)})); } else { localGraphics.drawLine(x, 0, x, hei); } } value += ss.getSeparation(); } } ss = new StepSize(analogHighValue, analogLowValue, 5); if (ss.getSeparation() != 0.0) { double value = ss.getLowValue(); for(;;) { if (value >= analogLowValue) { if (value > analogHighValue || value > ss.getHighValue()) break; int y = convertYDataToScreen(value); if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(vertAxisPos, y), new Point2D.Double(wid, y)})); } else { localGraphics.drawLine(vertAxisPos, y, wid, y); } } value += ss.getSeparation(); } } if (localGraphics != null) { localGraphics.setStroke(Highlight.solidLine); } } // draw all of the signals if (USE_ANTIALIASING && analog && localGraphics != null) { Object oldAntialiasing = localGraphics.getRenderingHint(RenderingHints.KEY_ANTIALIASING); localGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); processSignals(localGraphics, bounds, polys); localGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAntialiasing); } else { processSignals(localGraphics, bounds, polys); } // draw all of the control points if (localGraphics != null) processControlPoints(localGraphics, bounds); // draw the vertical label if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(vertAxisPos, 0), new Point2D.Double(vertAxisPos, hei)})); } else { localGraphics.setColor(new Color(User.getColor(User.ColorPrefType.WAVE_FOREGROUND))); localGraphics.drawLine(vertAxisPos, 0, vertAxisPos, hei); if (selected) { localGraphics.drawLine(vertAxisPos-1, 0, vertAxisPos-1, hei); localGraphics.drawLine(vertAxisPos-2, 0, vertAxisPos-2, hei-1); localGraphics.drawLine(vertAxisPos-3, 0, vertAxisPos-3, hei-2); } } if (analog) { double displayedLow = convertYScreenToData(hei); double displayedHigh = convertYScreenToData(0); StepSize ss = new StepSize(displayedHigh, displayedLow, 5); if (ss.getSeparation() != 0.0) { double value = ss.getLowValue(); if (localGraphics != null) localGraphics.setFont(waveWindow.getFont()); int lastY = -1; for(int i=0; ; i++) { if (value > displayedHigh) break; if (value >= displayedLow) { int y = convertYDataToScreen(value); if (lastY >= 0) { if (lastY - y > 100) { // add 5 tick marks for(int j=1; j<5; j++) { int intY = (lastY - y) / 5 * j + y; if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(vertAxisPos-5, intY), new Point2D.Double(vertAxisPos, intY)})); } else { localGraphics.drawLine(vertAxisPos-5, intY, vertAxisPos, intY); } } } else if (lastY - y > 25) { // add 1 tick mark int intY = (lastY - y) / 2 + y; if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(vertAxisPos-5, intY), new Point2D.Double(vertAxisPos, intY)})); } else { localGraphics.drawLine(vertAxisPos-5, intY, vertAxisPos, intY); } } } if (polys != null) { polys.add(new Poly(new Point2D[] { new Point2D.Double(vertAxisPos-10, y), new Point2D.Double(vertAxisPos, y)})); } else { localGraphics.drawLine(vertAxisPos-10, y, vertAxisPos, y); } String yValue = TextUtils.convertToEngineeringNotation(value, null); if (polys != null) { Poly poly = new Poly(new Point2D[] { new Point2D.Double(vertAxisPos-12, y)}); poly.setStyle(Poly.Type.TEXTRIGHT); poly.setTextDescriptor(TextDescriptor.EMPTY.withAbsSize(6)); poly.setString(yValue); polys.add(poly); } else { GlyphVector gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), yValue); Rectangle2D glyphBounds = gv.getLogicalBounds(); int height = (int)glyphBounds.getHeight(); int yPos = y + height / 2; if (yPos-height <= 0) yPos = height+1; if (yPos >= hei) yPos = hei; int xPos = vertAxisPos-10-(int)glyphBounds.getWidth()-2; if (xPos < 0) xPos = 0; localGraphics.drawString(yValue, xPos, yPos); } lastY = y; } value += ss.getSeparation(); } } } } void dumpDataCSV(PrintWriter pw) { for(WaveSignal ws : waveSignals.values()) { if (ws.getSignal() instanceof AnalogSignal) { AnalogSignal as = (AnalogSignal)ws.getSignal(); // AnalogAnalysis an = as.getAnalysis(); for (int s = 0, numSweeps = as.getNumSweeps(); s < numSweeps; s++) { pw.println(); Waveform wave = as.getWaveform(s); NewSignal.Approximation pref = wave.getPreferredApproximation(); NewSignal.Approximation waveform = pref /* FIXME */; int numEvents = waveform.getNumEvents(); for(int i=0; i<numEvents; i++) pw.println("\""+waveform.getTime(i) + "\""+ ","+ "\""+((ScalarSample)waveform.getSample(i)).getValue()+"\""); } } } } private static String pad2(String s) { return s.length()>=2 ? s : pad2("0"+s); } void dumpDataForGnuplot(PrintWriter pw) { dumpDataForGnuplot(pw, Double.MIN_VALUE, Double.MAX_VALUE, ""); } void dumpDataForGnuplot(PrintWriter pw, double min, double max, String sep) { boolean first = true; int linetype = 1; for(WaveSignal ws : waveSignals.values()) { boolean used = false; if (ws.getSignal() instanceof AnalogSignal) { AnalogSignal as = (AnalogSignal)ws.getSignal(); for (int s = 0, numSweeps = as.getNumSweeps(); s < numSweeps; s++) { if (!first) pw.print(sep); pw.print(" \'-\' with lines "); Color c = ws.getColor(); pw.print(" lt "+linetype+" "); pw.print("lc rgb \"#"+ pad2(Integer.toString(c.getRed() & 0xff, 16))+ pad2(Integer.toString(c.getGreen() & 0xff, 16))+ pad2(Integer.toString(c.getBlue() & 0xff, 16))+ "\" "); pw.print(" title \""+ws.getSignal().getFullName()+"\" "); first = false; used = true; } } if (used) linetype++; } for(WaveSignal ws : waveSignals.values()) { if (ws.getSignal() instanceof AnalogSignal) { AnalogSignal as = (AnalogSignal)ws.getSignal(); for (int s = 0, numSweeps = as.getNumSweeps(); s < numSweeps; s++) { pw.println(); Waveform wave = as.getWaveform(s); NewSignal.Approximation pref = wave.getPreferredApproximation(); NewSignal.Approximation waveform = pref /* FIXME */; int numEvents = waveform.getNumEvents(); for(int i=0; i<numEvents; i++) { if (waveform.getTime(i) < min || waveform.getTime(i) > max) continue; pw.println(waveform.getTime(i) + " " + ((ScalarSample)waveform.getSample(i)).getValue()); } pw.println("e"); pw.println(); } } } } private List<WaveSelection> processSignals(Graphics g, Rectangle2D bounds, List<PolyBase> forPs) { List<WaveSelection> selectedObjects = null; if (bounds != null) selectedObjects = new ArrayList<WaveSelection>(); int hei = sz.height; Signal xSignal = xAxisSignal; if (waveWindow.isXAxisLocked()) xSignal = waveWindow.getXAxisSignalAll(); double[] result = new double[3]; int linePointMode = waveWindow.getLinePointMode(); Collection<WaveSignal> sigs = waveSignals.values(); // // At the moment we have two simulation data storage // mechanisms: the old array-based mechanism and the new // BTree-based mechanism. Sometimes people forget which one // they have turned on, so until the BTree code replaces the // old code (ie until we delete the old code) we display this // reminder so people know which backend they are using and // can report bugs appropriately. This code will be removed // once the BTree mechanism replaces the old mechanism. // boolean isUsingBTrees = true; for(WaveSignal ws : sigs) { Signal sig = ws.getSignal(); if (!(sig instanceof AnalogSignal)) { isUsingBTrees = false; } else { Waveform wave = ((AnalogSignal)sig).getWaveform(0); if (!(wave instanceof BTreeNewSignal)) isUsingBTrees = false; } } if (Job.getDebug() && g!=null) { g.setColor(gridColor); g.setColor(Color.gray); g.setFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 10)); String msg = isUsingBTrees ? "(using new code)" : "(using legacy simulation code)"; g.drawString(msg, getWidth()-g.getFontMetrics().stringWidth(msg)-10, getHeight()-10 ); } int sigIndex = 0; for(WaveSignal ws : sigs) { if (g != null) { if (waveWindow.getPrintingMode() == 2) g.setColor(Color.BLACK); else g.setColor(ws.getColor()); } if (forPs != null) { double yPos = hei / 2; Poly.Type style = Poly.Type.TEXTRIGHT; if (sigs.size() > 1) { if (sigIndex == sigs.size()-1) style = Poly.Type.TEXTBOTRIGHT; else if (sigIndex == 0) style = Poly.Type.TEXTTOPRIGHT; yPos = ((double)(hei * sigIndex)) / (sigs.size()-1); } Poly poly = new Poly(new Point2D[] {new Point2D.Double(0, yPos)}); poly.setStyle(style); poly.setTextDescriptor(TextDescriptor.EMPTY.withAbsSize(12)); poly.setString(ws.getSignal().getFullName()); forPs.add(poly); } sigIndex++; if (ws.getSignal() instanceof AnalogSignal) { // draw analog trace AnalogSignal as = (AnalogSignal)ws.getSignal(); AnalogAnalysis an = as.getAnalysis(); for (int s = 0, numSweeps = as.getNumSweeps(); s < numSweeps; s++) { boolean included = waveWindow.isSweepSignalIncluded(an, s); if (!included) continue; Waveform wave = as.getWaveform(s); NewSignal.Approximation pref = wave.getPreferredApproximation(); NewSignal.Approximation waveform = (Simulation.isUseLegacySimulationCode() || wave instanceof WaveformImpl) ? pref : wave.getPixelatedApproximation(convertXScreenToData(0), convertXScreenToData(sz.width), sz.width); Waveform xWaveform = null; if (xSignal != null) xWaveform = ((AnalogSignal)xSignal).getWaveform(s); int lastX = 0, lastLY = 0, lastHY = 0; int numEvents = waveform.getNumEvents(); for(int i=0; i<numEvents; i++) { int x = convertXDataToScreen(waveform.getTime(i)); int lowY = convertYDataToScreen(((ScalarSample)waveform.getSample(i)).getValue()); int highY = convertYDataToScreen(((ScalarSample)waveform.getSample(i)).getValue()); if (xWaveform != null) { xWaveform.getEvent(i, result); x = convertXDataToScreen(result[1]); } // draw lines if requested and line is on-screen if (linePointMode <= 1 && x >= vertAxisPos && lastX < sz.width) { if (i != 0) { // drawing has lines if (processALine(g, lastX, lastLY, x, lowY, bounds, forPs, selectedObjects, ws, s)) break; if (lastLY != lastHY || lowY != highY) { if (processALine(g, lastX, lastHY, x, highY, bounds, forPs, selectedObjects, ws, s)) break; if (processALine(g, lastX, lastHY, x, lowY, bounds, forPs, selectedObjects, ws, s)) break; if (processALine(g, lastX, lastLY, x, highY, bounds, forPs, selectedObjects, ws, s)) break; } } if (an.extrapolateValues() && i == numEvents-1) { if (getMinXAxis() < getMaxXAxis()) { // process extrapolated line from the last data point if (processALine(g, x, lowY, sz.width, lowY, bounds, forPs, selectedObjects, ws, s)) break; if (lastLY != lastHY || lowY != highY) { if (processALine(g, x, highY, sz.width, highY, bounds, forPs, selectedObjects, ws, s)) break; } } } } // show points if requested and point is on-screen if (linePointMode >= 1 && x >= vertAxisPos && x <= sz.width) { if (processABox(g, x-2, lowY-2, x+2, lowY+2, bounds, forPs, selectedObjects, ws, false, 0)) break; } lastX = x; lastLY = lowY; lastHY = highY; } /* System.out.println("misses="+com.sun.electric.tool.simulation.NewSignalSimpleImpl.misses + ", "+ "avg steps="+ (((float)com.sun.electric.tool.simulation.NewSignalSimpleImpl.steps)/ com.sun.electric.tool.simulation.NewSignalSimpleImpl.numLookups)); com.sun.electric.tool.simulation.NewSignalSimpleImpl.misses=0; com.sun.electric.tool.simulation.NewSignalSimpleImpl.steps=0; com.sun.electric.tool.simulation.NewSignalSimpleImpl.numLookups=0; */ } continue; } if (ws.getSignal() instanceof DigitalSignal) { // draw digital traces DigitalSignal ds = (DigitalSignal)ws.getSignal(); DigitalAnalysis an = ds.getAnalysis(); List<DigitalSignal> bussedSignals = ds.getBussedSignals(); if (bussedSignals != null) { // a digital bus trace long curYValue = 0; double curXValue = 0; int lastX = vertAxisPos; for(;;) { double nextXValue = Double.MAX_VALUE; int bit = 0; boolean curDefined = true; for(Signal subSig : bussedSignals) { DigitalSignal subDS = (DigitalSignal)subSig; int numEvents = subDS.getNumEvents(); boolean undefined = false; for(int i=0; i<numEvents; i++) { double xValue = subDS.getTime(i); if (xValue <= curXValue) { switch (subDS.getState(i) & Stimuli.LOGIC) { case Stimuli.LOGIC_LOW: curYValue &= ~(1<<bit); undefined = false; break; case Stimuli.LOGIC_HIGH: curYValue |= (1<<bit); undefined = false; break; case Stimuli.LOGIC_X: case Stimuli.LOGIC_Z: undefined = true; break; } } else { if (xValue < nextXValue) nextXValue = xValue; break; } } if (undefined) { curDefined = false; break; } bit++; } int x = convertXDataToScreen(curXValue); if (x >= vertAxisPos) { if (x < vertAxisPos+5) { // on the left edge: just draw the "<" if (processALine(g, x, hei/2, x+5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; if (processALine(g, x, hei/2, x+5, 5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } else { // bus change point: draw the "X" if (processALine(g, x-5, 5, x+5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; if (processALine(g, x+5, 5, x-5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } if (lastX+5 < x-5) { // previous bus change point: draw horizontal bars to connect if (processALine(g, lastX+5, 5, x-5, 5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; if (processALine(g, lastX+5, hei-5, x-5, hei-5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } String valString = "XX"; if (curDefined) valString = Long.toString(curYValue); if (g != null) { g.setFont(waveWindow.getFont()); GlyphVector gv = waveWindow.getFont().createGlyphVector(waveWindow.getFontRenderContext(), valString); Rectangle2D glyphBounds = gv.getLogicalBounds(); int textHei = (int)glyphBounds.getHeight(); g.drawString(valString, x+2, hei/2+textHei/2); } if (forPs != null) { Point2D [] pts = new Point2D[1]; pts[0] = new Point2D.Double(x+2, hei/2); Poly poly = new Poly(pts); poly.setStyle(Poly.Type.TEXTLEFT); poly.setTextDescriptor(TextDescriptor.EMPTY.withAbsSize(8)); poly.setString(valString); forPs.add(poly); } } curXValue = nextXValue; lastX = x; if (nextXValue == Double.MAX_VALUE) break; } if (an.extrapolateValues()) { int wid = sz.width; if (lastX+5 < wid) { // run horizontal bars to the end if (processALine(g, lastX+5, 5, wid, 5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; if (processALine(g, lastX+5, hei-5, wid, hei-5, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } } continue; } // a simple digital signal int lastx = vertAxisPos; int lastState = 0; if (ds.getStateVector() == null) continue; int numEvents = ds.getNumEvents(); int lastLowy = 0, lastHighy = 0; for(int i=0; i<numEvents; i++) { double xValue = ds.getTime(i); int x = convertXDataToScreen(xValue); if (Simulation.isWaveformDisplayMultiState() && g != null) { if (waveWindow.getPrintingMode() == 2) g.setColor(Color.BLACK); else { switch (ds.getState(i) & Stimuli.STRENGTH) { case Stimuli.OFF_STRENGTH: g.setColor(waveWindow.getOffStrengthColor()); break; case Stimuli.NODE_STRENGTH: g.setColor(waveWindow.getNodeStrengthColor()); break; case Stimuli.GATE_STRENGTH: g.setColor(waveWindow.getGateStrengthColor()); break; case Stimuli.VDD_STRENGTH: g.setColor(waveWindow.getPowerStrengthColor()); break; } } } int state = ds.getState(i) & Stimuli.LOGIC; int lowy = 0, highy = 0; switch (state) { case Stimuli.LOGIC_HIGH: lowy = highy = 5; break; case Stimuli.LOGIC_LOW: lowy = highy = hei-5; break; case Stimuli.LOGIC_X: lowy = 5; highy = hei-5; break; case Stimuli.LOGIC_Z: lowy = (hei-10) / 3 + 5; highy = hei - (hei-10) / 3 - 5; break; } if (g != null && !Simulation.isWaveformDisplayMultiState()) g.setColor(Color.RED); if (i != 0) { if (state != lastState) { if (processALine(g, x, Math.min(lowy, lastLowy), x, Math.max(lowy, lastLowy), bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } } if (g != null && !Simulation.isWaveformDisplayMultiState()) { if (lastState == Stimuli.LOGIC_Z) g.setColor(Color.GREEN); } if (lastLowy == lastHighy) { if (processALine(g, lastx, lastLowy, x, lastLowy, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } else { if (processABox(g, lastx, lastLowy, x, lastHighy, bounds, forPs, selectedObjects, ws, false, 0)) return selectedObjects; } if (an.extrapolateValues()) { if (i >= numEvents-1) { if (g != null && !Simulation.isWaveformDisplayMultiState()) { if (state == Stimuli.LOGIC_Z) g.setColor(Color.GREEN); else g.setColor(Color.RED); } int wid = sz.width; if (lowy == highy) { if (processALine(g, x, lowy, wid-1, lowy, bounds, forPs, selectedObjects, ws, -1)) return selectedObjects; } else { if (processABox(g, x, lowy, wid-1, highy, bounds, forPs, selectedObjects, ws, false, 0)) return selectedObjects; } } } lastx = x; lastLowy = lowy; lastHighy = highy; lastState = state; } } } return selectedObjects; } private List<WaveSelection> processControlPoints(Graphics g, Rectangle2D bounds) { List<WaveSelection> selectedObjects = null; if (bounds != null) selectedObjects = new ArrayList<WaveSelection>(); // show control points for(WaveSignal ws : waveSignals.values()) { if (g != null) g.setColor(ws.getColor()); Double [] points = ws.getSignal().getControlPoints(); if (points == null) continue; if (g != null) g.setColor(ws.getColor()); for(int i=0; i<points.length; i++) { double xValue = points[i].doubleValue(); int x = convertXDataToScreen(xValue); if (processABox(g, x-CONTROLPOINTSIZE, sz.height-CONTROLPOINTSIZE*2, x+CONTROLPOINTSIZE, sz.height, bounds, null, selectedObjects, ws, true, xValue)) break; // see if the control point is selected boolean found = false; if (bounds == null && ws.getSelectedControlPoints() != null) { for(int j=0; j<ws.getSelectedControlPoints().length; j++) if (ws.getSelectedControlPoints()[j] == xValue) { found = true; break; } } if (found) { g.setColor(Color.GREEN); if (processABox(g, x-CONTROLPOINTSIZE+2, sz.height-CONTROLPOINTSIZE*2+2, x+CONTROLPOINTSIZE-2, sz.height-2, bounds, null, selectedObjects, ws, true, xValue)) break; g.setColor(ws.getColor()); } } } return selectedObjects; } private boolean processABox(Graphics g, int lX, int lY, int hX, int hY, Rectangle2D bounds, List<PolyBase> forPs, List<WaveSelection> result, WaveSignal ws, boolean controlPoint, double controlXValue) { // bounds is non-null if doing hit-testing if (bounds != null) { // do bounds checking for hit testing if (hX > bounds.getMinX() && lX < bounds.getMaxX() && hY > bounds.getMinY() && lY < bounds.getMaxY()) { if (forPs != null) { PolyBase poly = new PolyBase((lX+hX)/2, (lY+hY)/2, hX-lX, hY-lY); poly.setStyle(Poly.Type.FILLED); poly.setLayer(Artwork.tech().defaultLayer); forPs.add(poly); return false; } WaveSelection wSel = new WaveSelection(); wSel.ws = ws; wSel.controlPoint = controlPoint; wSel.controlXValue = controlXValue; result.add(wSel); return true; } return false; } // clip to left edge if (hX <= vertAxisPos) return false; if (lX < vertAxisPos) lX = vertAxisPos; // not doing hit-testing, just doing drawing g.fillRect(lX, lY, hX-lX, hY-lY); return false; } private boolean processALine(Graphics g, int fX, int fY, int tX, int tY, Rectangle2D bounds, List<PolyBase> forPs, List<WaveSelection> result, WaveSignal ws, int sweepNum) { if (bounds != null) { // do bounds checking for hit testing Point2D from = new Point2D.Double(fX, fY); Point2D to = new Point2D.Double(tX, tY); if (!GenMath.clipLine(from, to, bounds.getMinX(), bounds.getMaxX(), bounds.getMinY(), bounds.getMaxY())) { if (forPs != null) { forPs.add(new PolyBase(new Point2D[] {from, to})); return false; } WaveSelection wSel = new WaveSelection(); wSel.ws = ws; wSel.controlPoint = false; result.add(wSel); return true; } return false; } // clip to left edge if (fX < vertAxisPos || tX < vertAxisPos) { Point2D from = new Point2D.Double(fX, fY); Point2D to = new Point2D.Double(tX, tY); if (GenMath.clipLine(from, to, vertAxisPos, sz.width, 0, sz.height)) return false; fX = (int)from.getX(); fY = (int)from.getY(); tX = (int)to.getX(); tY = (int)to.getY(); } // draw the line g.drawLine(fX, fY, tX, tY); // highlight the line if requested boolean highlighted = ws.isHighlighted(); if (ws.getPanel().waveWindow.getHighlightedSweep() >= 0) { highlighted = ws.getPanel().waveWindow.getHighlightedSweep() == sweepNum; } if (highlighted) { if (fX == tX) { // vertical line g.drawLine(fX-1, fY, tX-1, tY); g.drawLine(fX+1, fY, tX+1, tY); } else if (fY == tY) { // horizontal line g.drawLine(fX, fY+1, tX, tY+1); g.drawLine(fX, fY-1, tX, tY-1); } else { int xDelta = 0, yDelta = 1; if (Math.abs(fX-tX) < Math.abs(fY-tY)) { xDelta = 1; yDelta = 0; } g.drawLine(tX+xDelta, tY+yDelta, fX+xDelta, fY+yDelta); g.drawLine(tX-xDelta, tY-yDelta, fX-xDelta, fY-yDelta); } } return false; } // ************************************* SIGNAL SELECTION ************************************* /** * the MouseListener events */ public void mousePressed(MouseEvent evt) { requestFocus(); waveWindow.vcrClickStop(); // set this to be the selected panel makeSelectedPanel(evt.getX(), evt.getY()); // reset dragging from last time for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); if (wp.draggingArea) wp.repaintContents(); wp.draggingArea = false; } if (evt.getClickCount() == 2 && evt.getX() < vertAxisPos) { new WaveformZoom(Main.getCurrentJFrame(), analogLowValue, analogHighValue, minXPosition, maxXPosition, waveWindow, this); return; } ToolBar.CursorMode mode = ToolBar.getCursorMode(); if (ClickZoomWireListener.isRightMouse(evt)) { if ((evt.getModifiersEx()&MouseEvent.SHIFT_DOWN_MASK) != 0) mode = ToolBar.CursorMode.ZOOM; else { if (evt.getX() < vertAxisPos) { // right click in ruler area: show popup of choices JPopupMenu menu = new JPopupMenu(); JMenuItem item = new JMenuItem("Linear"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { makeLinear(); } }); menu.add(item); item = new JMenuItem("Logarithmic"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { makeLogarithmic(); } }); menu.add(item); menu.show(this, evt.getX(), evt.getY()); return; } } } for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) it.next().curMeasurement = null; if (mode == ToolBar.CursorMode.ZOOM) mousePressedZoom(evt); else if (mode == ToolBar.CursorMode.PAN) mousePressedPan(evt); else mousePressedSelect(evt); } public void mouseReleased(MouseEvent evt) { ToolBar.CursorMode mode = ToolBar.getCursorMode(); if (ClickZoomWireListener.isRightMouse(evt) && (evt.getModifiersEx()&MouseEvent.SHIFT_DOWN_MASK) != 0) mode = ToolBar.CursorMode.ZOOM; if (mode == ToolBar.CursorMode.ZOOM) mouseReleasedZoom(evt); else if (mode == ToolBar.CursorMode.PAN) mouseReleasedPan(evt); else mouseReleasedSelect(evt); for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel panel = it.next(); panel.curMeasurement = null; if (mode == ToolBar.CursorMode.MEASURE) panel.draggingArea = false; } } public void mouseClicked(MouseEvent evt) {} public void mouseEntered(MouseEvent evt) { curPanel = this; curXPos = evt.getX(); } public void mouseExited(MouseEvent evt) { curPanel = null; } /** * the MouseMotionListener events */ public void mouseMoved(MouseEvent evt) { curXPos = evt.getX(); ToolBar.CursorMode mode = ToolBar.getCursorMode(); if (mode == ToolBar.CursorMode.ZOOM) mouseMovedZoom(evt); else if (mode == ToolBar.CursorMode.PAN) mouseMovedPan(evt); else mouseMovedSelect(evt); } public void mouseDragged(MouseEvent evt) { curXPos = evt.getX(); ToolBar.CursorMode mode = ToolBar.getCursorMode(); if (ClickZoomWireListener.isRightMouse(evt) && (evt.getModifiersEx()&MouseEvent.SHIFT_DOWN_MASK) != 0) mode = ToolBar.CursorMode.ZOOM; if (mode == ToolBar.CursorMode.ZOOM) mouseDraggedZoom(evt); else if (mode == ToolBar.CursorMode.PAN) mouseDraggedPan(evt); else mouseDraggedSelect(evt); } /** * the MouseWheelListener events */ public void mouseWheelMoved(MouseWheelEvent evt) {} /** * the KeyListener events */ public void keyPressed(KeyEvent evt) { waveWindow.vcrClickStop(); } public void keyReleased(KeyEvent evt) {} public void keyTyped(KeyEvent evt) {} private void digitalSignalNameClicked(ActionEvent evt) { long delay = evt.getWhen() - lastClick; lastClick = evt.getWhen(); if (delay < Main.getDoubleClickSpeed()) { toggleBusContents(); return; } Set<JButton> set = waveSignals.keySet(); if (set.size() == 0) return; JButton but = set.iterator().next(); WaveSignal ws = waveSignals.get(but); if ((evt.getModifiers()&MouseEvent.SHIFT_MASK) == 0) { // standard click: add this as the only trace for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); wp.clearHighlightedSignals(); } addHighlightedSignal(ws, true); makeSelectedPanel(-1, -1); } else { // shift click: add or remove to list of highlighted traces if (ws.isHighlighted()) removeHighlightedSignal(ws, true); else addHighlightedSignal(ws, true); } // show it in the schematic waveWindow.crossProbeWaveformToEditWindow(); } /** * Method to find the Signals in an area. * @param lX the low X coordinate of the area. * @param hX the high X coordinate of the area. * @param lY the low Y coordinate of the area. * @param hY the high Y coordinate of the area. * @return a list of WaveSelection objects. */ private List<WaveSelection> findSignalsInArea(int lX, int hX, int lY, int hY) { double lXd = Math.min(lX, hX)-2; double hXd = Math.max(lX, hX)+2; double hYd = Math.min(lY, hY)-2; double lYd = Math.max(lY, hY)+2; if (lXd > hXd) { double swap = lXd; lXd = hXd; hXd = swap; } if (lYd > hYd) { double swap = lYd; lYd = hYd; hYd = swap; } Rectangle2D bounds = new Rectangle2D.Double(lXd, lYd, hXd-lXd, hYd-lYd); List<WaveSelection> sigs = processSignals(null, bounds, null); List<WaveSelection> cps = processControlPoints(null, bounds); for(WaveSelection ws : sigs) cps.add(ws); return cps; } /** * Method to find a list of PolyBase objects that describe Signals in this panel. * @return a list of PolyBase objects. */ public List<PolyBase> getPolysForPrinting() { if (!szValid) { for(Iterator<Panel> it = this.waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); if (wp.szValid) { sz = wp.sz; szValid = true; break; } } } sz = getSize(); List<PolyBase> polys = new ArrayList<PolyBase>(); drawPanelContents(sz.width, sz.height, null, new Rectangle2D.Double(vertAxisPos, 0, sz.width, sz.height), polys); return polys; } private static class WaveSelection { /** Selected signal in Waveform Window */ WaveSignal ws; /** true if this is a control point */ boolean controlPoint; /** X value of the control point (if a CP) */ double controlXValue; } /** * Method to remove all displayed measurements from the panel */ public void clearMeasurements() { measurementList.clear(); curMeasurement = null; repaintContents(); } /** * Method to implement the Mouse Pressed event for selection. */ public void mousePressedSelect(MouseEvent evt) { // see if the horizontal cursors are selected draggingMain = draggingExt = draggingVertAxis = false; int mainX = convertXDataToScreen(waveWindow.getMainXPositionCursor()); if (Math.abs(mainX - evt.getX()) < 5) { draggingMain = true; return; } int extX = convertXDataToScreen(waveWindow.getExtensionXPositionCursor()); if (Math.abs(extX - evt.getX()) < 5) { draggingExt = true; return; } if (Math.abs(vertAxisPos - evt.getX()) < 5) { draggingVertAxis = true; return; } // drag area Point pt = new Point(evt.getX(), evt.getY()); if (ToolBar.getCursorMode() == ToolBar.CursorMode.MEASURE) { pt = snapPoint(pt); } double xV = convertXScreenToData(pt.x); double yV = convertYScreenToData(pt.y); if (ToolBar.getCursorMode() == ToolBar.CursorMode.MEASURE) { if (ClickZoomWireListener.isRightMouse(evt)) { measurementList.clear(); curMeasurement = null; return; } curMeasurement = new Rectangle2D.Double(xV, yV, 0, 0); measurementList.add(curMeasurement); } dragEndXD = dragStartXD = xV; dragEndYD = dragStartYD = yV; draggingArea = true; } private Point snapPoint(Point pt) { // snap to any waveform points for(WaveSignal ws : waveSignals.values()) { if (!(ws.getSignal() instanceof AnalogSignal)) continue; // draw analog trace AnalogSignal as = (AnalogSignal)ws.getSignal(); double[] result = new double[3]; AnalogAnalysis an = as.getAnalysis(); for(int s=0, numSweeps = as.getNumSweeps(); s<numSweeps; s++) { if (!waveWindow.isSweepSignalIncluded(an, s)) continue; Waveform waveform = as.getWaveform(s); int numEvents = waveform.getNumEvents(); for(int i=0; i<numEvents; i++) { waveform.getEvent(i, result); int x = convertXDataToScreen(result[0]); int lowY = convertYDataToScreen(result[1]); int highY = convertYDataToScreen(result[2]); if (Math.abs(x - pt.x) < 5 && pt.y > lowY - 5 && pt.y < highY + 5) { pt.x = x; pt.y = Math.max(Math.min(pt.y, highY), lowY); return pt; } } } } // snap to any waveform lines Point2D snap = new Point2D.Double(pt.x, pt.y); for(WaveSignal ws : waveSignals.values()) { if (!(ws.getSignal() instanceof AnalogSignal)) continue; // draw analog trace AnalogSignal as = (AnalogSignal)ws.getSignal(); double[] result = new double[3]; double[] lastResult = new double[3]; AnalogAnalysis an = as.getAnalysis(); for(int s=0, numSweeps = as.getNumSweeps(); s<numSweeps; s++) { if (!waveWindow.isSweepSignalIncluded(an, s)) continue; Waveform waveform = as.getWaveform(s); int numEvents = waveform.getNumEvents(); waveform.getEvent(0, lastResult); Point2D lastPt = new Point2D.Double(convertXDataToScreen(lastResult[0]), convertYDataToScreen((lastResult[1] + lastResult[2]) / 2)); for(int i=1; i<numEvents; i++) { waveform.getEvent(i, result); Point2D thisPt = new Point2D.Double(convertXDataToScreen(result[0]), convertYDataToScreen((result[1] + result[2]) / 2)); Point2D closest = GenMath.closestPointToSegment(lastPt, thisPt, snap); if (closest.distance(snap) < 5) { pt.x = (int)Math.round(closest.getX()); pt.y = (int)Math.round(closest.getY()); break; } lastPt = thisPt; } } } // no snapping: return the original point return pt; } /** * Method to implement the Mouse Released event for selection. */ public void mouseReleasedSelect(MouseEvent evt) { if (draggingArea) { draggingArea = false; Panel wp = (Panel)evt.getSource(); if (ToolBar.getCursorMode() != ToolBar.CursorMode.MEASURE && ToolBar.getSelectMode() == ToolBar.SelectMode.OBJECTS) { List<WaveSelection> selectedObjects = wp.findSignalsInArea(convertXDataToScreen(dragStartXD), convertXDataToScreen(dragEndXD), convertYDataToScreen(dragStartYD), convertYDataToScreen(dragEndYD)); if ((evt.getModifiers()&MouseEvent.SHIFT_MASK) == 0) { // standard click: add this as the only trace if (analog) clearHighlightedSignals(); else { for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel oWp = it.next(); oWp.clearHighlightedSignals(); } } for(WaveSelection wSel : selectedObjects) { if (wSel.controlPoint) { wSel.ws.addSelectedControlPoint(wSel.controlXValue); } wp.addHighlightedSignal(wSel.ws, false); } } else { // shift click: add or remove to list of highlighted traces for(WaveSelection wSel : selectedObjects) { WaveSignal ws = wSel.ws; if (ws.isHighlighted()) { if (wSel.controlPoint) ws.removeSelectedControlPoint(wSel.controlXValue); removeHighlightedSignal(ws, false); } else { if (wSel.controlPoint) ws.addSelectedControlPoint(wSel.controlXValue); wp.addHighlightedSignal(ws, false); } } } // show it in the schematic wp.waveWindow.crossProbeWaveformToEditWindow(); } else { // just leave this highlight and show dimensions } } repaintContents(); } /** * Method to implement the Mouse Dragged event for selection. */ public void mouseDraggedSelect(MouseEvent evt) { if (draggingMain) { if (evt.getX() <= 0) return; double value = convertXScreenToData(evt.getX()); waveWindow.setMainXPositionCursor(value); waveWindow.repaintAllPanels(); // waveWindow.redrawAllPanels(); } else if (draggingExt) { if (evt.getX() <= 0) return; double value = convertXScreenToData(evt.getX()); waveWindow.setExtensionXPositionCursor(value); waveWindow.repaintAllPanels(); // waveWindow.redrawAllPanels(); } else if (draggingVertAxis) { if (evt.getX() <= 0) return; if (waveWindow.isXAxisLocked()) { for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); wp.vertAxisPos = evt.getX(); } waveWindow.redrawAllPanels(); waveWindow.getMainHorizRuler().repaint(); } else { vertAxisPos = evt.getX(); repaintWithRulers(); } } else if (draggingArea) { Set<Panel> measureWindows = new HashSet<Panel>(); Point cPt = new Point(); Panel curPanel = this; measureWindows.add(curPanel); if (WaveformWindow.USETABLES) { JTable table = waveWindow.getWaveformTable(); // find the current Panel int startPanel = 0; for(int i=0; i<table.getRowCount(); i++) { if (this == waveWindow.getPanel(i)) { startPanel = i; break; } } // find the panel with the coordinates int yp = evt.getY(); Rectangle bou = evt.getComponent().getBounds(); if (yp >= bou.y && yp < bou.y+bou.height) { cPt.setLocation(evt.getX(), evt.getY()); } else { int curPanelNum = startPanel; if (yp < bou.y) { while (yp < bou.y) { // negative coordinate: try a previous panel if (curPanelNum <= 0) break; curPanelNum--; measureWindows.add(waveWindow.getPanel(curPanelNum)); yp += table.getRowHeight(curPanelNum); } } else if (yp >= bou.y+bou.height) { while (yp >= bou.y+bou.height) { // coordinate too large: try a subsequent panel if (curPanelNum+1 >= table.getRowCount()) break; yp -= table.getRowHeight(curPanelNum); curPanelNum++; measureWindows.add(waveWindow.getPanel(curPanelNum)); } } curPanel = waveWindow.getPanel(curPanelNum); cPt.setLocation(evt.getX(), yp); } } else { cPt.setLocation(evt.getX(), evt.getY()); } // snap to waveform point curPanel.snapPoint(cPt); dragEndXD = curPanel.convertXScreenToData(cPt.x); dragEndYD = curPanel.convertYScreenToData(cPt.y); if (ToolBar.getCursorMode() == ToolBar.CursorMode.MEASURE && !ClickZoomWireListener.isRightMouse(evt)) { // reset all panels for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) it.next().draggingArea = false; // update all windows the measurement may have crossed over for(Panel wp : measureWindows) { if (wp.curMeasurement == null) { wp.curMeasurement = new Rectangle2D.Double(); wp.measurementList.add(wp.curMeasurement); } wp.curMeasurement.setRect(dragStartXD, dragStartYD, dragEndXD-dragStartXD, dragEndYD-dragStartYD); wp.dragStartXD = dragStartXD; wp.dragStartYD = dragStartYD; wp.dragEndXD = dragEndXD; wp.dragEndYD = dragEndYD; wp.draggingArea = true; wp.repaint(); // wp.repaintContents(); } } repaint(); // repaintContents(); } } public void mouseMovedSelect(MouseEvent evt) { // see if over horizontal cursors int mainX = convertXDataToScreen(waveWindow.getMainXPositionCursor()); int extX = convertXDataToScreen(waveWindow.getExtensionXPositionCursor()); if (Math.abs(mainX - evt.getX()) < 5 || Math.abs(extX - evt.getX()) < 5 || Math.abs(vertAxisPos - evt.getX()) < 5) { setCursor(dragXPositionCursor); } else { setCursor(Cursor.getDefaultCursor()); } } // ************************************* HIGHLIGHTING ************************************* public void clearHighlightedSignals() { for(WaveSignal ws : waveSignals.values()) { if (!ws.isHighlighted()) continue; ws.setHighlighted(false); ws.clearSelectedControlPoints(); if (ws.getButton() != null) ws.getButton().setBackground(background); } waveWindow.setHighlightedSweep(-1); repaintContents(); } public void addHighlightedSignal(WaveSignal ws, boolean repaintContents) { if (ws.getButton() != null) { if (background == null) background = ws.getButton().getBackground(); ws.getButton().setBackground(new Color(User.getColor(User.ColorPrefType.WAVE_BACKGROUND))); } ws.setHighlighted(true); waveWindow.setHighlightedSweep(-1); if (repaintContents) repaintContents(); } public void removeHighlightedSignal(WaveSignal ws, boolean repaintContents) { ws.setHighlighted(false); if (ws.getButton() != null) ws.getButton().setBackground(background); waveWindow.setHighlightedSweep(-1); if (repaintContents) repaintContents(); } public boolean isHidden() { return hidden; } public void setHidden(boolean hidden) { this.hidden = hidden; } /** * Method to make this the highlighted Panel. */ public void makeSelectedPanel(int x, int y) { for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); if (wp.selected && wp != this) { wp.selected = false; wp.repaintContents(); } } if (!selected) { selected = true; repaintContents(); } curPanel = this; curXPos = x; } public boolean isSelected() { return selected; } // ****************************** ZOOMING AND PANNING ****************************** /** * Method to implement the Mouse Pressed event for zooming. */ public void mousePressedZoom(MouseEvent evt) { dragStartXD = convertXScreenToData(evt.getX()); dragStartYD = convertYScreenToData(evt.getY()); ZoomAndPanListener.setProperCursor(evt); draggingArea = true; } /** * Method to implement the Mouse Released event for zooming. */ public void mouseReleasedZoom(MouseEvent evt) { ZoomAndPanListener.setProperCursor(evt); draggingArea = false; double lowXValue = Math.min(dragEndXD, dragStartXD); double highXValue = Math.max(dragEndXD, dragStartXD); double xRange = highXValue - lowXValue; lowXValue -= xRange / 8; highXValue += xRange / 8; double lowValue = Math.min(dragEndYD, dragStartYD); double highValue = Math.max(dragEndYD, dragStartYD); double valueRange = highValue - lowValue; lowValue -= valueRange / 8; highValue += valueRange / 8; for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); if (!waveWindow.isXAxisLocked() && wp != this) continue; if ((evt.getModifiers()&MouseEvent.SHIFT_MASK) == 0 || ClickZoomWireListener.isRightMouse(evt)) { // standard click: zoom in if (wp.getMinXAxis() > wp.getMaxXAxis()) wp.setXAxisRange(highXValue, lowXValue); else wp.setXAxisRange(lowXValue, highXValue); if (wp == this) wp.setYAxisRange(lowValue, highValue); } else { // shift-click: zoom out double oldRange = wp.maxXPosition - wp.minXPosition; double min = (lowXValue + highXValue) / 2 - oldRange; double max = (lowXValue + highXValue) / 2 + oldRange; if (wp.getMinXAxis() > wp.getMaxXAxis()) wp.setXAxisRange(max, min); else wp.setXAxisRange(min, max); if (wp == this) wp.setYAxisRange((lowValue + highValue) / 2 - wp.analogRange, (lowValue + highValue) / 2 + wp.analogRange); } wp.repaintWithRulers(); } } /** * Method to implement the Mouse Dragged event for zooming. */ public void mouseDraggedZoom(MouseEvent evt) { // ZoomAndPanListener.setProperCursor(evt); if (draggingArea) { dragEndXD = convertXScreenToData(evt.getX()); dragEndYD = convertYScreenToData(evt.getY()); repaint(); // repaintContents(); } } public void mouseMovedZoom(MouseEvent evt) { // ZoomAndPanListener.setProperCursor(evt); } /** * Method to implement the Mouse Pressed event for panning. */ public void mousePressedPan(MouseEvent evt) { dragStartXD = convertXScreenToData(evt.getX()); dragStartYD = convertYScreenToData(evt.getY()); } /** * Method to implement the Mouse Released event for panning. */ public void mouseReleasedPan(MouseEvent evt) { } /** * Method to implement the Mouse Dragged event for panning. */ public void mouseDraggedPan(MouseEvent evt) { dragEndXD = convertXScreenToData(evt.getX()); double dXValue = dragEndXD - dragStartXD; dragEndYD = convertYScreenToData(evt.getY()); double dYValue = dragEndYD - dragStartYD; for(Iterator<Panel> it = waveWindow.getPanels(); it.hasNext(); ) { Panel wp = it.next(); if (!waveWindow.isXAxisLocked() && wp != this) continue; wp.setXAxisRange(wp.minXPosition - dXValue, wp.maxXPosition - dXValue); if (wp == this) setYAxisRange(analogLowValue - dYValue, analogHighValue - dYValue); wp.repaintWithRulers(); } dragStartXD = dragEndXD; dragStartYD = dragEndYD; } public void mouseMovedPan(MouseEvent evt) {} // ************************************* DRAG AND DROP ************************************* /** * Class to extend a JLabel so that it is draggable. */ private static class DragLabel extends JLabel implements DragGestureListener, DragSourceListener { private DragSource dragSource; public DragLabel(String s) { setText(s); dragSource = DragSource.getDefaultDragSource(); dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); } public void dragGestureRecognized(DragGestureEvent e) { // make the Transferable Object Transferable transferable = new StringSelection("PANEL " + getText()); // begin the drag dragSource.startDrag(e, DragSource.DefaultLinkDrop, transferable, this); } public void dragEnter(DragSourceDragEvent e) {} public void dragOver(DragSourceDragEvent e) {} public void dragExit(DragSourceEvent e) {} public void dragDropEnd(DragSourceDropEvent e) {} public void dropActionChanged (DragSourceDragEvent e) {} } }